From nobody Sat Feb 7 16:06:08 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD16622D4EB; Thu, 27 Feb 2025 12:36:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659799; cv=none; b=DRQdf9Te0DPcJ59TChSFY+VtUzDpnSzuzeN5Ohob1dCfEWZBd34lHhb1r0gUwpECHoSKvFSuAuc993gEUaLllIo6dNzQkJdjPVudbofcSu94Wai5gjSbGoEI5OpnJWjuM6dQwPpxkCPaM4dMCSBtVloZ8x8Ud6BeY5VEjN2AvsY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659799; c=relaxed/simple; bh=iH024xRD6cyG1uXtxu6ZtfNK2dVVhqaMlYLdesMgGzs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IKHr/71dfZEm1/00afiZpaauglWFCM3brDGi7lcQcf+4xm5vsIR4Z+WRs+NnC4CXykRuTgI4TAkNev4y64i+8DGsTDXGvEJxGHA6V8tvBT7+wH3+5S1ynYL+4s49p44EgdsBfvF333zIamL5ofaViZB8dBwfvAyxEBjPKYbotiY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=D4bPdPBq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="D4bPdPBq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EA7EAC4CEE7; Thu, 27 Feb 2025 12:36:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740659798; bh=iH024xRD6cyG1uXtxu6ZtfNK2dVVhqaMlYLdesMgGzs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=D4bPdPBqUn5LzwalPZyesL3WxozFSx+lVm60/h5twQ9xSKynYeJm2FdJtGtxpAE31 LQvmq2Sryqt5NNtK6pQx6Li0UTxSl5PB1JdOzCMb+ntGrQrntpi3Mo78dLz2brYA6O ZnnDvG6RsPJWzLI2a7SAlG382hA357b2dq0i5X1VRBlTo5Yg7civaLgJwk4FG86Nn1 qm1ERKfA1k4jJ7C00uDIp2ZaBcLP07H63ls0cOyJ+iua/q0wLgvd9cBxq/4GHlaPkP FJ9Bs99jJlUtBGBBpZSg7NRoNxA2f0CST2FjJGbP75f2m3YMyqx+KCQ5vqQyOyFb+A MFvLXHD8gbe0g== From: Andreas Hindborg Date: Thu, 27 Feb 2025 13:35:10 +0100 Subject: [PATCH v5 1/4] rust: sync: change ` as ForeignOwnable>::PointedTo` to `T` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250227-configfs-v5-1-c40e8dc3b9cd@kernel.org> References: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> In-Reply-To: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> To: Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Joel Becker , Peter Zijlstra , Ingo Molnar , Will Deacon , Waiman Long , Fiona Behrens , Charalampos Mitrodimas , Daniel Almeida Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3633; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=iH024xRD6cyG1uXtxu6ZtfNK2dVVhqaMlYLdesMgGzs=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBnwFwCLx7ibrR86EZs9lxtw3xWU7qjzlrraFiUg aqWtCvx2j+JAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ8BcAgAKCRDhuBo+eShj d7zCD/4kXPykk11x9sM9lc2DgfeaLln/UR8q1SJcAmmtkL/DKMGOmk/3Xu655gnCckR7WujxadZ gkJ18bqmT4iM1OLMxu6YgiANSD1mwE8IeK7m/VHvEJIDTfckWZNT2h6iGOxUnwWSzWPZ+SrbyKH W2VUPvuJL2SGBfO13ZoBAcwNgLTlINOBWmIdgv5NuYtqhraWKol4ieNUdR4G3nxHV+MUsE1ALnn aMc+LTKOCp/AyNhMGUim1WCCn9FlYDGo4t0DAy34bcwMre4A+e41ViEXLPqgD8uTm03Mp2D9O7Y TVDhQSv44L53L93xhiu7eaUL04DQGgKeERR/80bHME3zuWpb1KBPCy3salC/wRAnd9LQvHzEzHQ LnF5o7K2jYwHiB+zfm7YX0JpGxHZ/bb7GIVUJWJqHm/mQ38vcLAnODSLr08jg83EL37WNBVfBy2 VfPti6ubyex7wsi+236SpG1fsmubcy6VZ3gWRQnb9REYLD6S/rSjpe8ydnVznUfuYmbFbdDTX9q pAMbMQfteYk4IQeQPOGdbvPHlBVc/+ykqewg0GQqet0/qoLFUvy/CFjFKHuQjrc1WXnK/SCZQ8G vsiNGoKQns5Vz1BV4Inyk6TF/1kT49pc5Q9eawot4dWRLSIMZuyj78/SGcsUng1qbVEk7e5VU4j qDB72JaC/YFS1uA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Using `ArcInner` as `PoinedTo` in the `ForeignOwnable` implementation for `Arc` is a bit unfortunate. Using `T` as `PointedTo` does not remove any functionality, but allows `ArcInner` to be private. Further, it allows downstream users to write code that is generic over `Box` and `Arc`, when downstream users need access to `T` after calling `into_foreign`. Reviewed-by: Fiona Behrens Reviewed-by: Daniel Almeida Tested-by: Daniel Almeida Signed-off-by: Andreas Hindborg Reviewed-by: Tamir Duberstein --- This patch is a dependency for Rust `configfs` abstractions. It allows both `Box` and `Arc` to be used as pointer types in the `configfs` hierarchy. --- rust/kernel/sync/arc.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index dfe4abf82c25..3d77a31e116f 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -143,7 +143,7 @@ pub struct Arc { #[doc(hidden)] #[pin_data] #[repr(C)] -pub struct ArcInner { +struct ArcInner { refcount: Opaque, data: T, } @@ -345,18 +345,25 @@ pub fn into_unique_or_drop(self) -> Option>> { =20 // SAFETY: The `into_foreign` function returns a pointer that is well-alig= ned. unsafe impl ForeignOwnable for Arc { - type PointedTo =3D ArcInner; + type PointedTo =3D T; type Borrowed<'a> =3D ArcBorrow<'a, T>; type BorrowedMut<'a> =3D Self::Borrowed<'a>; =20 fn into_foreign(self) -> *mut Self::PointedTo { - ManuallyDrop::new(self).ptr.as_ptr() + let this =3D ManuallyDrop::new(self).ptr.as_ptr(); + // SAFETY: `x` is a valid pointer to `Self` so the projection belo= w is + // in bounds of the allocation. + unsafe { core::ptr::addr_of_mut!((*this).data) } } =20 unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> Self { + // SAFETY: We did the reverse offset calculation in `into_foreign`= , so + // the offset calculation below is in bounds of the allocation. + let inner_ptr =3D unsafe { kernel::container_of!(ptr, ArcInner,= data).cast_mut() }; + // SAFETY: The safety requirements of this function ensure that `p= tr` comes from a previous // call to `Self::into_foreign`. - let inner =3D unsafe { NonNull::new_unchecked(ptr) }; + let inner =3D unsafe { NonNull::new_unchecked(inner_ptr) }; =20 // SAFETY: By the safety requirement of this function, we know tha= t `ptr` came from // a previous call to `Arc::into_foreign`, which guarantees that `= ptr` is valid and @@ -365,9 +372,13 @@ unsafe fn from_foreign(ptr: *mut Self::PointedTo) -> S= elf { } =20 unsafe fn borrow<'a>(ptr: *mut Self::PointedTo) -> ArcBorrow<'a, T> { + // SAFETY: We did the reverse offset calculation in `into_foreign`= , so + // the offset calculation below is in bounds of the allocation. + let inner_ptr =3D unsafe { kernel::container_of!(ptr, ArcInner,= data).cast_mut() }; + // SAFETY: The safety requirements of this function ensure that `p= tr` comes from a previous // call to `Self::into_foreign`. - let inner =3D unsafe { NonNull::new_unchecked(ptr) }; + let inner =3D unsafe { NonNull::new_unchecked(inner_ptr) }; =20 // SAFETY: The safety requirements of `from_foreign` ensure that t= he object remains alive // for the lifetime of the returned value. --=20 2.47.0 From nobody Sat Feb 7 16:06:08 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 858B627003F; Thu, 27 Feb 2025 12:36:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659777; cv=none; b=A+OZAJ/vmSA9+BzkYBdE67OrvpNqoerGEpuxmpDcbYNLpdvt/L5BK6On2Ea/xYJy6iGT4FNinlx20isnXebuXXMxPKoATwQD+RL5kNHG6SOvhTDBIYq4QwAxp/OHK997I0X2sNg7RR6MFD6+goiGZlWQfEpdgCvQmdAF9VU9HM0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659777; c=relaxed/simple; bh=hLYbrPzLDCrE0+Aglcg97zP7Qt6F/MUoWfTfYlPdKdc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SceqDa245QZNKj3Bi7uzdT4OFq888gwD/BASNhWZaAXqXvA3xydIWY52RegDmLHUoHhwwFutrwej6qh32rwiVjUiDpEwPQmGTEZcPZXFhEzWMr9w7hWNq8i2ShGho2LtDAIgIV03aLPZZJCVEZl9dV2ks73NB3kDTLq8VX9UjBc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=E6+WY/AB; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="E6+WY/AB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7A3DBC4CEDD; Thu, 27 Feb 2025 12:36:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740659777; bh=hLYbrPzLDCrE0+Aglcg97zP7Qt6F/MUoWfTfYlPdKdc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=E6+WY/ABGUWAg73AwODlHTteG6lNfoQ+LgmD18QSqY8n/WsgRWNBNAyhugxjn/Mpi WQQmbqyLZFUhVl6RRiw8ylVCKNGJotmVrInkrV+dgO/T0LeCstHiifqO1fyvDRhsOe dJwbfrKykI31841EZjORPeR+8ut4czAvJoyhXDihgEc2fh641yvY85T4u5dRvkrxur 09j+pSAVR1zJFbqz6YIq7vqVNG593vhsdqK4y51n8mAjvBmCqSomIDq2xPvJp2tWyF Ius34HG8PGRFngT9ER4rV8evbt/xNzj39TYIh9IPpFGZfcFEyYlxGsOaOeDkgXqPbl 9qDzWVlZWX5kg== From: Andreas Hindborg Date: Thu, 27 Feb 2025 13:35:11 +0100 Subject: [PATCH v5 2/4] rust: configfs: introduce rust support for configfs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250227-configfs-v5-2-c40e8dc3b9cd@kernel.org> References: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> In-Reply-To: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> To: Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Joel Becker , Peter Zijlstra , Ingo Molnar , Will Deacon , Waiman Long , Fiona Behrens , Charalampos Mitrodimas , Daniel Almeida Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=41273; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=hLYbrPzLDCrE0+Aglcg97zP7Qt6F/MUoWfTfYlPdKdc=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBnwFwDYZkNlKGkDWPgfLWzBbdJHZEQ+9xoGPEYt wJDejnB+UGJAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ8BcAwAKCRDhuBo+eShj d9c8EACG9tUHgfKmkCYgb1hUaTk447o7aCM1muDmuf7FeJm54EX+Pc7tiGlQgppzhbKLUGWSwZB TQmRbyFne4KCdnFxIsiN4VdnbZZZ2HjIo5REz4Fkkg/QRLE5AmQSixnjqc0vV9qDbDmzJ5W3ZZD JLy8f/ird3FyjF0IqyAsrmJf1MDiW9yYmu7URrnMvmkI/z6zPA6G/2/6pqLvmSZq1HdYd/FhAS3 yJjITWG60tqUlrGTpcuHWurchShIwSSGAEQSpX57g6BHvJZw7yS/vB4Got3Bd119jRGKu695e/a VbsTy3HtZ/0CuwyL1j/zve9MRq2s6azbGbqBdanT6MrLrCXlcSInirdCUJt21V0+cTZvXm4phAr 5sPNTuBHSJR2X+TQYx/IANf/MXaFXFw4eGY/eKvvtxSM9QrgxcjfCi1I7wzlvLKNPr1DkcLuhAC zwO5MwpeN9geTxXx3CUxswAeK1oPqjzskf4JDsusJ4hloWl7k2U2NRsd5B1NOiP1S13QXlXZ2Yz 1ayjTnsU/hs8UxbIav3cfqqhKkV7LNQbQPxMO/ieN0an1s3TMLAESCm4/hV8TuX5V+VW1ZKZwaY M5PVvfi524ReiABftBqfRX6NUGbhtlzkHd7ykScLs3wLkmDqjqtOs2PSP9JlqNCr4RtlD2XH1zb hZaBqLb/x5U1kbQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a Rust API for configfs, thus allowing Rust modules to use configfs for configuration. Make the implementation a shim on top of the C configfs implementation, allowing safe use of the C infrastructure from Rust. Signed-off-by: Andreas Hindborg --- This patch is a direct dependency for `rnull`, the rust null block driver. --- rust/bindings/bindings_helper.h | 1 + rust/helpers/mutex.c | 5 + rust/kernel/configfs.rs | 1054 +++++++++++++++++++++++++++++++++++= ++++ rust/kernel/lib.rs | 2 + 4 files changed, 1062 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 55354e4dec14..d115a770306f 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/mutex.c b/rust/helpers/mutex.c index 06575553eda5..3e9b910a88e9 100644 --- a/rust/helpers/mutex.c +++ b/rust/helpers/mutex.c @@ -17,3 +17,8 @@ void rust_helper_mutex_assert_is_held(struct mutex *mutex) { lockdep_assert_held(mutex); } + +void rust_helper_mutex_destroy(struct mutex *lock) +{ + mutex_destroy(lock); +} diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs new file mode 100644 index 000000000000..78a6dbd5534b --- /dev/null +++ b/rust/kernel/configfs.rs @@ -0,0 +1,1054 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! `configfs` interface. +//! +//! `configfs` is an in-memory pseudo file system for configuration of ker= nel +//! modules. Please see the [C documentation] for details and intended use= of +//! `configfs`. +//! +//! This module does not support the following `configfs` features: +//! +//! - Items. All group children are groups. +//! - Symlink support. +//! - `disconnect_notify` hook. +//! - Default groups. +//! +//! See the [rust_configfs.rs] sample for a full example use of this modul= e. +//! +//! C header: [`include/linux/configfs.h`](srctree/include/linux/configfs.= h) +//! +//! # Example +//! +//! ```ignore +//! use kernel::alloc::flags; +//! use kernel::c_str; +//! use kernel::configfs_attrs; +//! use kernel::configfs; +//! use kernel::new_mutex; +//! use kernel::page::PAGE_SIZE; +//! use kernel::sync::Mutex; +//! use kernel::ThisModule; +//! +//! #[pin_data] +//! struct RustConfigfs { +//! #[pin] +//! config: configfs::Subsystem, +//! } +//! +//! impl kernel::InPlaceModule for RustConfigfs { +//! fn init(_module: &'static ThisModule) -> impl PinInit= { +//! pr_info!("Rust configfs sample (init)\n"); +//! +//! let item_type =3D configfs_attrs! { +//! container: configfs::Subsystem, +//! data: Configuration, +//! attributes: [ +//! message: 0, +//! bar: 1, +//! ], +//! }; +//! +//! try_pin_init!(Self { +//! config <- configfs::Subsystem::new( +//! c_str!("rust_configfs"), item_type, Configuration::new= () +//! ), +//! }) +//! } +//! } +//! +//! #[pin_data] +//! struct Configuration { +//! message: &'static CStr, +//! #[pin] +//! bar: Mutex<(KBox<[u8; PAGE_SIZE]>, usize)>, +//! } +//! +//! impl Configuration { +//! fn new() -> impl PinInit { +//! try_pin_init!(Self { +//! message: c_str!("Hello World\n"), +//! bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KE= RNEL)?, 0)), +//! }) +//! } +//! } +//! +//! #[vtable] +//! impl configfs::AttributeOperations<0> for Configuration { +//! type Data =3D Configuration; +//! +//! fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> = Result { +//! pr_info!("Show message\n"); +//! let data =3D container.message; +//! page[0..data.len()].copy_from_slice(data); +//! Ok(data.len()) +//! } +//! } +//! +//! #[vtable] +//! impl configfs::AttributeOperations<1> for Configuration { +//! type Data =3D Configuration; +//! +//! fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> = Result { +//! pr_info!("Show bar\n"); +//! let guard =3D container.bar.lock(); +//! let data =3D guard.0.as_slice(); +//! let len =3D guard.1; +//! page[0..len].copy_from_slice(&data[0..len]); +//! Ok(len) +//! } +//! +//! fn store(container: &Configuration, page: &[u8]) -> Result { +//! pr_info!("Store bar\n"); +//! let mut guard =3D container.bar.lock(); +//! guard.0[0..page.len()].copy_from_slice(page); +//! guard.1 =3D page.len(); +//! Ok(()) +//! } +//! } +//! ``` +//! +//! [C documentation]: srctree/Documentation/filesystems/configfs.rst +//! [rust_configfs.rs]: srctree/samples/rust/rust_configfs.rs + +use crate::alloc::flags; +use crate::container_of; +use crate::page::PAGE_SIZE; +use crate::prelude::*; +use crate::str::CString; +use crate::sync::Arc; +use crate::sync::ArcBorrow; +use crate::types::ForeignOwnable; +use crate::types::Opaque; +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::ptr::addr_of; +use core::ptr::addr_of_mut; + +/// A `configfs` subsystem. +/// +/// This is the top level entrypoint for a `configfs` hierarchy. To regist= er +/// with configfs, embed a field of this type into your kernel module stru= ct. +#[pin_data(PinnedDrop)] +pub struct Subsystem { + #[pin] + subsystem: Opaque, + #[pin] + data: Data, +} + +// SAFETY: We do not provide any operations on `Subsystem`. +unsafe impl Sync for Subsystem {} + +// SAFETY: Ownership of `Subsystem` can safely be transferred to other thr= eads. +unsafe impl Send for Subsystem {} + +impl Subsystem { + /// Create an initializer for a [`Subsystem`]. + /// + /// The subsystem will appear in configfs as a directory name given by + /// `name`. The attributes available in directory are specified by + /// `item_type`. + pub fn new( + name: &'static CStr, + item_type: &'static ItemType, Data>, + data: impl PinInit, + ) -> impl PinInit { + try_pin_init!(Self { + subsystem <- kernel::init::zeroed().chain( + |place: &mut Opaque| { + // SAFETY: We initialized the required fields of `plac= e.group` above. + unsafe { + bindings::config_group_init_type_name( + &mut (*place.get()).su_group, + name.as_ptr(), + item_type.as_ptr(), + ) + }; + + // SAFETY: `place.su_mutex` is valid for use as a mute= x. + unsafe { + bindings::__mutex_init( + &mut (*place.get()).su_mutex, + kernel::optional_name!().as_char_ptr(), + kernel::static_lock_class!().as_ptr(), + ) + } + Ok(()) + } + ), + data <- data, + }) + .pin_chain(|this| { + crate::error::to_result( + // SAFETY: We initialized `this.subsystem` according to C = API contract above. + unsafe { bindings::configfs_register_subsystem(this.subsys= tem.get()) }, + ) + }) + } +} + +#[pinned_drop] +impl PinnedDrop for Subsystem { + fn drop(self: Pin<&mut Self>) { + // SAFETY: We registered `self.subsystem` in the initializer retur= ned by `Self::new`. + unsafe { bindings::configfs_unregister_subsystem(self.subsystem.ge= t()) }; + // SAFETY: We initialized the mutex in `Subsystem::new`. + unsafe { bindings::mutex_destroy(addr_of_mut!((*self.subsystem.get= ()).su_mutex)) }; + } +} + +/// Trait that allows offset calculations for structs that embed a +/// `bindings::config_group`. +/// +/// Users of the `configfs` API should not need to implement this trait. +/// +/// # Safety +/// +/// - Implementers of this trait must embed a `bindings::config_group`. +/// - Methods must be implemented according to method documentation. +pub unsafe trait HasGroup { + /// Return the address of the `bindings::config_group` embedded in `Se= lf`. + /// + /// # Safety + /// + /// - `this` must be a valid allocation of at least the size of `Self`. + unsafe fn group(this: *const Self) -> *const bindings::config_group; + + /// Return the address of the `Self` that `group` is embedded in. + /// + /// # Safety + /// + /// - `group` must point to the `bindings::config_group` that is embed= ded in + /// `Self`. + unsafe fn container_of(group: *const bindings::config_group) -> *const= Self; +} + +// SAFETY: `Subsystem` embeds a field of type `bindings::config_grou= p` +// within the `subsystem` field. +unsafe impl HasGroup for Subsystem { + unsafe fn group(this: *const Self) -> *const bindings::config_group { + // SAFETY: By impl and function safety requirement this projection= is in bounds. + unsafe { addr_of!((*(*this).subsystem.get()).su_group) } + } + + unsafe fn container_of(group: *const bindings::config_group) -> *const= Self { + // SAFETY: By impl and function safety requirement this projection= is in bounds. + let c_subsys_ptr =3D unsafe { container_of!(group, bindings::confi= gfs_subsystem, su_group) }; + let opaque_ptr =3D c_subsys_ptr.cast::>(); + // SAFETY: By impl and function safety requirement, `opaque_ptr` a= nd the + // pointer it returns, are within the same allocation. + unsafe { container_of!(opaque_ptr, Subsystem, subsystem) } + } +} + +/// A `configfs` group. +/// +/// To add a subgroup to `configfs`, pass this type as `ctype` to +/// [`crate::configfs_attrs`] when creating a group in [`GroupOperations::= make_group`]. +#[pin_data] +pub struct Group { + #[pin] + group: Opaque, + #[pin] + data: Data, +} + +impl Group { + /// Create an initializer for a new group. + /// + /// When instantiated, the group will appear as a directory with the n= ame + /// given by `name` and it will contain attributes specified by `item_= type`. + pub fn new( + name: CString, + item_type: &'static ItemType, Data>, + data: impl PinInit, + ) -> impl PinInit { + try_pin_init!(Self { + group <- kernel::init::zeroed().chain(|v: &mut Opaque| { + let place =3D v.get(); + let name =3D name.as_bytes_with_nul().as_ptr(); + // SAFETY: It is safe to initialize a group once it has be= en zeroed. + unsafe { + bindings::config_group_init_type_name(place, name.cast= (), item_type.as_ptr()) + }; + Ok(()) + }), + data <- data, + }) + } +} + +// SAFETY: `Group` embeds a field of type `bindings::config_group` +// within the `group` field. +unsafe impl HasGroup for Group { + unsafe fn group(this: *const Self) -> *const bindings::config_group { + Opaque::raw_get( + // SAFETY: By impl and function safety requirements this field + // projection is within bounds of the allocation. + unsafe { addr_of!((*this).group) }, + ) + } + + unsafe fn container_of(group: *const bindings::config_group) -> *const= Self { + let opaque_ptr =3D group.cast::>(); + // SAFETY: By impl and function safety requirement, `opaque_ptr` a= nd + // pointer it returns will be in the same allocation. + unsafe { container_of!(opaque_ptr, Self, group) } + } +} + +/// # Safety +/// +/// `this` must be a valid pointer. +/// +/// If `this` does not represent the root group of a `configfs` subsystem, +/// `this` must be a pointer to a `bindings::config_group` embedded in a +/// `Group`. +/// +/// 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`. +unsafe fn get_group_data<'a, Parent>(this: *mut bindings::config_group) ->= &'a Parent { + // SAFETY: `this` is a valid pointer. + let is_root =3D unsafe { (*this).cg_subsys.is_null() }; + + if !is_root { + // SAFETY: By C API contact,`this` was returned from a call to + // `make_group`. The pointer is known to be embedded within a + // `Group`. + unsafe { &(*Group::::container_of(this)).data } + } else { + // SAFETY: By C API contract, `this` is a pointer to the + // `bindings::config_group` field within a `Subsystem`. + unsafe { &(*Subsystem::container_of(this)).data } + } +} + +struct GroupOperationsVTable(PhantomData<(Parent, Child)>); + +impl GroupOperationsVTable +where + Parent: GroupOperations, + Child: 'static, +{ + /// # Safety + /// + /// `this` must be a valid pointer. + /// + /// If `this` does not represent the root group of a `configfs` subsys= tem, + /// `this` must be a pointer to a `bindings::config_group` embedded in= a + /// `Group`. + /// + /// Otherwise, `this` must be a pointer to a `bindings::config_group` = that + /// is embedded in a `bindings::configfs_subsystem` that is embedded i= n a + /// `Subsystem`. + /// + /// `name` must point to a null terminated string. + unsafe extern "C" fn make_group( + this: *mut bindings::config_group, + name: *const kernel::ffi::c_char, + ) -> *mut bindings::config_group { + // SAFETY: By function safety requirements of this function, this = call + // is safe. + let parent_data =3D unsafe { get_group_data(this) }; + + let group_init =3D match Parent::make_group( + parent_data, + // SAFETY: By function safety requirements, name points to a n= ull + // terminated string. + unsafe { CStr::from_char_ptr(name) }, + ) { + Ok(init) =3D> init, + Err(e) =3D> return e.to_ptr(), + }; + + let child_group =3D > as InPlaceInit= >>::try_pin_init( + group_init, + flags::GFP_KERNEL, + ); + + match child_group { + Ok(child_group) =3D> { + let child_group_ptr =3D child_group.into_foreign(); + // SAFETY: We allocated the pointee of `child_ptr` above a= s a + // `Group`. + unsafe { Group::::group(child_group_ptr) }.cast_mut= () + } + Err(e) =3D> e.to_ptr(), + } + } + + /// # Safety + /// + /// If `this` does not represent the root group of a `configfs` subsys= tem, + /// `this` must be a pointer to a `bindings::config_group` embedded in= a + /// `Group`. + /// + /// Otherwise, `this` must be a pointer to a `bindings::config_group` = that + /// is embedded in a `bindings::configfs_subsystem` that is embedded i= n a + /// `Subsystem`. + /// + /// `item` must point to a `bindings::config_item` within a + /// `bindings::config_group` within a `Group`. + unsafe extern "C" fn drop_item( + this: *mut bindings::config_group, + item: *mut bindings::config_item, + ) { + // SAFETY: By function safety requirements of this function, this = call + // is safe. + let parent_data =3D unsafe { get_group_data(this) }; + + // SAFETY: By function safety requirements, `item` is embedded in a + // `config_group`. + let c_child_group_ptr =3D unsafe { container_of!(item, bindings::c= onfig_group, cg_item) }; + // SAFETY: By function safety requirements, `c_child_group_ptr` is + // embedded within a `Group`. + let r_child_group_ptr =3D unsafe { Group::::container_of(c_= child_group_ptr) }; + + if Parent::HAS_DROP_ITEM { + Parent::drop_item( + parent_data, + // SAFETY: We called `into_foreign` to produce `r_child_gr= oup_ptr` in + // `make_group`. There are not other borrows of this point= er in existence. + unsafe { + > as ForeignOwnable>::borrow(r_child_= group_ptr.cast_mut()) + }, + ); + } + + // SAFETY: By C API contract, we are required to drop a refcount on + // `item`. + unsafe { bindings::config_item_put(item) }; + } + + const VTABLE: bindings::configfs_group_operations =3D bindings::config= fs_group_operations { + make_item: None, + make_group: Some(Self::make_group), + disconnect_notify: None, + drop_item: Some(Self::drop_item), + is_visible: None, + is_bin_visible: None, + }; + + const fn vtable_ptr() -> *const bindings::configfs_group_operations { + &Self::VTABLE as *const bindings::configfs_group_operations + } +} + +struct ItemOperationsVTable(PhantomData<(Container, Data)= >); + +impl ItemOperationsVTable, Data> +where + Data: 'static, +{ + /// # Safety + /// + /// `this` must be a pointer to a `bindings::config_group` embedded in= a + /// `Group`. + /// + /// This function will destroy the pointee of `this`. The pointee of `= this` + /// must not be accessed after the function returns. + unsafe extern "C" fn release(this: *mut bindings::config_item) { + // SAFETY: By function safety requirements, `this` is embedded in a + // `config_group`. + let c_group_ptr =3D unsafe { kernel::container_of!(this, bindings:= :config_group, cg_item) }; + // SAFETY: By function safety requirements, `c_group_ptr` is + // embedded within a `Group`. + let r_group_ptr =3D unsafe { Group::::container_of(c_group_p= tr) }; + + // SAFETY: We called `into_foreign` on `r_group_ptr` in + // `make_group`. + let pin_self =3D + unsafe { > as ForeignOwnable>::from_foreign(r_= group_ptr.cast_mut()) }; + drop(pin_self); + } + + const VTABLE: bindings::configfs_item_operations =3D bindings::configf= s_item_operations { + release: Some(Self::release), + allow_link: None, + drop_link: None, + }; + + const fn vtable_ptr() -> *const bindings::configfs_item_operations { + &Self::VTABLE as *const bindings::configfs_item_operations + } +} + +impl ItemOperationsVTable, Data> { + const VTABLE: bindings::configfs_item_operations =3D bindings::configf= s_item_operations { + release: None, + allow_link: None, + drop_link: None, + }; + + const fn vtable_ptr() -> *const bindings::configfs_item_operations { + &Self::VTABLE as *const bindings::configfs_item_operations + } +} + +/// Operations implemented by `configfs` groups that can create subgroups. +/// +/// Implement this trait on structs that embed a [`Subsystem`] or a [`Grou= p`]. +#[vtable] +pub trait GroupOperations { + /// The child data object type. + /// + /// This group will create subgroups (subdirectories) backed by this k= ind of + /// object. + type Child: 'static; + + /// Creates a new subgroup. + /// + /// The kernel will call this method in response to `mkdir(2)` in the + /// directory representing `this`. + /// + /// To accept the request to create a group, implementations should + /// return an initializer of a `Group`. To prevent creati= on, + /// return a suitable error. + fn make_group(&self, name: &CStr) -> Result, Error>>; + + /// Prepares the group for removal from configfs. + /// + /// The kernel will call this method before the directory representing= `_child` is removed from + /// `configfs`. + /// + /// Implementations can use this method to do house keeping before `co= nfigfs` drops its + /// reference to `Child`. + /// + /// NOTE: "drop" in the name of this function is not related to the Ru= st drop term. Rather, the + /// name is inherited from the callback name in the underlying C code. + fn drop_item(&self, _child: ArcBorrow<'_, Group>) { + kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR) + } +} + +/// A `configfs` attribute. +/// +/// An attribute appears as a file in configfs, inside a folder that repre= sent +/// the group that the attribute belongs to. +#[repr(transparent)] +pub struct Attribute { + attribute: Opaque, + _p: PhantomData<(O, Data)>, +} + +// SAFETY: We do not provide any operations on `Attribute`. +unsafe impl Sync for Attribute {} + +// SAFETY: Ownership of `Attribute` can safely be transferred to other thr= eads. +unsafe impl Send for Attribute {} + +impl Attribute +where + O: AttributeOperations, +{ + /// # Safety + /// + /// `item` must be embedded in a `bindings::config_group`. + /// + /// If `item` does not represent the root group of a `configfs` subsys= tem, + /// the group must be embedded in a `Group`. + /// + /// Otherwise, the group must be a embedded in a + /// `bindings::configfs_subsystem` that is embedded in a `Subsystem`. + /// + /// `page` must point to a writable buffer of size at least [`PAGE_SIZ= E`]. + unsafe extern "C" fn show( + item: *mut bindings::config_item, + page: *mut kernel::ffi::c_char, + ) -> isize { + let c_group: *mut bindings::config_group =3D + // SAFETY: By function safety requirements, `item` is embedded in a + // `config_group`. + unsafe { container_of!(item, bindings::config_group, cg_item) = }.cast_mut(); + + // SAFETY: The function safety requirements for this function sati= sfy + // the conditions for this call. + let data: &Data =3D unsafe { get_group_data(c_group) }; + + // SAFETY: By function safety requirements, `page` is writable for= `PAGE_SIZE`. + let ret =3D O::show(data, unsafe { &mut *(page as *mut [u8; PAGE_S= IZE]) }); + + match ret { + Ok(size) =3D> size as isize, + Err(err) =3D> err.to_errno() as isize, + } + } + + /// # Safety + /// + /// `item` must be embedded in a `bindings::config_group`. + /// + /// If `item` does not represent the root group of a `configfs` subsys= tem, + /// the group must be embedded in a `Group`. + /// + /// Otherwise, the group must be a embedded in a + /// `bindings::configfs_subsystem` that is embedded in a `Subsystem`. + /// + /// `page` must point to a readable buffer of size at least `size`. + unsafe extern "C" fn store( + item: *mut bindings::config_item, + page: *const kernel::ffi::c_char, + size: usize, + ) -> isize { + let c_group: *mut bindings::config_group =3D + // SAFETY: By function safety requirements, `item` is embedded in a + // `config_group`. + unsafe { container_of!(item, bindings::config_group, cg_item) = }.cast_mut(); + + // SAFETY: The function safety requirements for this function sati= sfy + // the conditions for this call. + let data: &Data =3D unsafe { get_group_data(c_group) }; + + let ret =3D O::store( + data, + // SAFETY: By function safety requirements, `page` is readable + // for at least `size`. + unsafe { core::slice::from_raw_parts(page.cast(), size) }, + ); + + match ret { + Ok(()) =3D> size as isize, + Err(err) =3D> err.to_errno() as isize, + } + } + + /// Create a new attribute. + /// + /// The attribute will appear as a file with name given by `name`. + pub const fn new(name: &'static CStr) -> Self { + Self { + attribute: Opaque::new(bindings::configfs_attribute { + ca_name: name.as_char_ptr(), + ca_owner: core::ptr::null_mut(), + ca_mode: 0o660, + show: Some(Self::show), + store: if O::HAS_STORE { + Some(Self::store) + } else { + None + }, + }), + _p: PhantomData, + } + } +} + +/// Operations supported by an attribute. +/// +/// Implement this trait on type and pass that type as generic parameter w= hen +/// creating an [`Attribute`]. The type carrying the implementation serve = no +/// purpose other than specifying the attribute operations. +/// +/// This trait must be implemented on the `Data` type of for types that +/// implement `HasGroup`. The trait must be implemented once for each +/// attribute of the group. The constant type parameter `ID` maps the +/// implementation to a specific `Attribute`. `ID` must be passed when dec= laring +/// attributes via the `configfs_attrs!` macro, to tie `AttributeOperation= s` +/// implementations to concrete named attributes. +#[vtable] +pub trait AttributeOperations { + /// The type of the object that contains the field that is backing the + /// attribute for this operation. + type Data; + + /// Renders the value of an attribute. + /// + /// This function is called by the kernel to read the value of an attr= ibute. + /// + /// Implementations should write the rendering of the attribute to `pa= ge` + /// and return the number of bytes written. + fn show(data: &Self::Data, page: &mut [u8; PAGE_SIZE]) -> Result; + + /// Stores the value of an attribute. + /// + /// This function is called by the kernel to update the value of an at= tribute. + /// + /// Implementations should parse the value from `page` and update inte= rnal + /// state to reflect the parsed value. + fn store(_data: &Self::Data, _page: &[u8]) -> Result { + kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR) + } +} + +/// A list of attributes. +/// +/// This type is used to construct a new [`ItemType`]. It represents a lis= t of +/// [`Attribute`] that will appear in the directory representing a [`Group= `]. +/// Users should not directly instantiate this type, rather they should us= e the +/// [`kernel::configfs_attrs`] macro to declare a static set of attributes= for a +/// group. +/// +/// # Note +/// +/// This type is constructed statically at compile time and is by the +/// [`kernel::configfs_attrs`] macro. +#[repr(transparent)] +pub struct AttributeList( + /// Null terminated Array of pointers to `Attribute`. The type is `c_v= oid` + /// to conform to the C API. + UnsafeCell<[*mut kernel::ffi::c_void; N]>, + PhantomData, +); + +// SAFETY: Ownership of `AttributeList` can safely be transferred to other= threads. +unsafe impl Send for AttributeList {} + +// SAFETY: We do not provide any operations on `AttributeList` that need s= ynchronization. +unsafe impl Sync for AttributeList {} + +impl AttributeList { + /// # Safety + /// + /// This function must only be called by the `configfs_attrs` + /// macro. + #[doc(hidden)] + pub const unsafe fn new() -> Self { + Self(UnsafeCell::new([core::ptr::null_mut(); N]), PhantomData) + } + + /// # Safety + /// + /// This function can only be called by the `configfs_attrs` + /// macro. + #[doc(hidden)] + pub const unsafe fn add( + &'static self, + attribute: &'static Attribute, + ) where + O: AttributeOperations, + { + // We need a space at the end of our list for a null terminator. + if I >=3D N - 1 { + kernel::build_error!("Invalid attribute index"); + } + + // SAFETY: This function is only called through the `configfs_attr= s` + // macro. This ensures that we are evaluating the function in const + // context when initializing a static. As such, the reference crea= ted + // below will be exclusive. + unsafe { + (&mut *self.0.get())[I] =3D (attribute as *const Attribute) + .cast_mut() + .cast() + }; + } +} + +/// A representation of the attributes that will appear in a [`Group`] or +/// [`Subsystem`]. +/// +/// Users should not directly instantiate objects of this type. Rather, th= ey +/// should use the [`kernel::configfs_attrs`] macro to statically declare = the +/// shape of a [`Group`] or [`Subsystem`]. +#[pin_data] +pub struct ItemType { + #[pin] + item_type: Opaque, + _p: PhantomData<(Container, Data)>, +} + +// SAFETY: We do not provide any operations on `ItemType` that need synchr= onization. +unsafe impl Sync for ItemType {} + +// SAFETY: Ownership of `ItemType` can safely be transferred to other thre= ads. +unsafe impl Send for ItemType {} + +macro_rules! impl_item_type { + ($tpe:ty) =3D> { + impl ItemType<$tpe, Data> { + #[doc(hidden)] + pub const fn new_with_child_ctor( + owner: &'static ThisModule, + attributes: &'static AttributeList, + ) -> Self + where + Data: GroupOperations, + Child: 'static, + { + Self { + item_type: Opaque::new(bindings::config_item_type { + ct_owner: owner.as_ptr(), + ct_group_ops: GroupOperationsVTable::= ::vtable_ptr().cast_mut(), + ct_item_ops: ItemOperationsVTable::<$tpe, Data>::v= table_ptr().cast_mut(), + ct_attrs: (attributes as *const AttributeList) + .cast_mut() + .cast(), + ct_bin_attrs: core::ptr::null_mut(), + }), + _p: PhantomData, + } + } + + #[doc(hidden)] + pub const fn new( + owner: &'static ThisModule, + attributes: &'static AttributeList, + ) -> Self { + Self { + item_type: Opaque::new(bindings::config_item_type { + ct_owner: owner.as_ptr(), + ct_group_ops: core::ptr::null_mut(), + ct_item_ops: ItemOperationsVTable::<$tpe, Data>::v= table_ptr().cast_mut(), + ct_attrs: (attributes as *const AttributeList) + .cast_mut() + .cast(), + ct_bin_attrs: core::ptr::null_mut(), + }), + _p: PhantomData, + } + } + } + }; +} + +impl_item_type!(Subsystem); +impl_item_type!(Group); + +impl ItemType { + fn as_ptr(&self) -> *const bindings::config_item_type { + self.item_type.get() + } +} + +/// Define a list of configfs attributes statically. +/// +/// Invoking the macro in the following manner: +/// +/// ```ignore +/// let item_type =3D configfs_attrs! { +/// container: configfs::Subsystem, +/// data: Configuration, +/// child: Child, +/// attributes: [ +/// message: 0, +/// bar: 1, +/// ], +/// }; +/// ``` +/// +/// Expands the following output: +/// +/// ```ignore +/// let item_type =3D { +/// static CONFIGURATION_MESSAGE_ATTR: kernel::configfs::Attribute< +/// 0, +/// Configuration, +/// Configuration, +/// > =3D unsafe { +/// kernel::configfs::Attribute::new({ +/// const S: &str =3D "message\u{0}"; +/// const C: &kernel::str::CStr =3D match kernel::str::CStr::f= rom_bytes_with_nul( +/// S.as_bytes() +/// ) { +/// Ok(v) =3D> v, +/// Err(_) =3D> { +/// core::panicking::panic_fmt(core::const_format_args= !( +/// "string contains interior NUL" +/// )); +/// } +/// }; +/// C +/// }) +/// }; +/// +/// static CONFIGURATION_BAR_ATTR: kernel::configfs::Attribute< +/// 1, +/// Configuration, +/// Configuration +/// > =3D unsafe { +/// kernel::configfs::Attribute::new({ +/// const S: &str =3D "bar\u{0}"; +/// const C: &kernel::str::CStr =3D match kernel::str::CStr::f= rom_bytes_with_nul( +/// S.as_bytes() +/// ) { +/// Ok(v) =3D> v, +/// Err(_) =3D> { +/// core::panicking::panic_fmt(core::const_format_args= !( +/// "string contains interior NUL" +/// )); +/// } +/// }; +/// C +/// }) +/// }; +/// +/// const N: usize =3D (1usize + (1usize + 0usize)) + 1usize; +/// +/// static CONFIGURATION_ATTRS: kernel::configfs::AttributeList =3D +/// unsafe { kernel::configfs::AttributeList::new() }; +/// +/// { +/// const N: usize =3D 0usize; +/// unsafe { CONFIGURATION_ATTRS.add::(&CONFIGURATION_MES= SAGE_ATTR) }; +/// } +/// +/// { +/// const N: usize =3D (1usize + 0usize); +/// unsafe { CONFIGURATION_ATTRS.add::(&CONFIGURATION_BAR= _ATTR) }; +/// } +/// +/// static CONFIGURATION_TPE: +/// kernel::configfs::ItemType ,C= onfiguration> +/// =3D kernel::configfs::ItemType::< +/// configfs::Subsystem, +/// Configuration +/// >::new_with_child_ctor::( +/// &THIS_MODULE, +/// &CONFIGURATION_ATTRS +/// ); +/// +/// &CONFIGURATION_TPE +/// } +/// ``` +#[macro_export] +macro_rules! configfs_attrs { + ( + container: $container:ty, + data: $data:ty, + attributes: [ + $($name:ident: $attr:literal),* $(,)? + ] $(,)? + ) =3D> { + $crate::configfs_attrs!( + count: + @container($container), + @data($data), + @child(), + @no_child(x), + @attrs($($name $attr)*), + @eat($($name $attr,)*), + @assign(), + @cnt(0usize), + ) + }; + ( + container: $container:ty, + data: $data:ty, + child: $child:ty, + attributes: [ + $($name:ident: $attr:literal),* $(,)? + ] $(,)? + ) =3D> { + $crate::configfs_attrs!( + count: + @container($container), + @data($data), + @child($child), + @no_child(), + @attrs($($name $attr)*), + @eat($($name $attr,)*), + @assign(), + @cnt(0usize), + ) + }; + (count: + @container($container:ty), + @data($data:ty), + @child($($child:ty)?), + @no_child($($no_child:ident)?), + @attrs($($aname:ident $aattr:literal)*), + @eat($name:ident $attr:literal, $($rname:ident $rattr:literal,)*), + @assign($($assign:block)*), + @cnt($cnt:expr), + ) =3D> { + $crate::configfs_attrs!( + count: + @container($container), + @data($data), + @child($($child)?), + @no_child($($no_child)?), + @attrs($($aname $aattr)*), + @eat($($rname $rattr,)*), + @assign($($assign)* { + const N: usize =3D $cnt; + // The following macro text expands to a call to `Attribut= e::add`. + // SAFETY: We are expanding `configfs_attrs`. + unsafe { + $crate::macros::paste!( + [< $data:upper _ATTRS >] + .add::(&[< $data:upper _ $name:up= per _ATTR >]) + ) + }; + }), + @cnt(1usize + $cnt), + ) + }; + (count: + @container($container:ty), + @data($data:ty), + @child($($child:ty)?), + @no_child($($no_child:ident)?), + @attrs($($aname:ident $aattr:literal)*), + @eat(), + @assign($($assign:block)*), + @cnt($cnt:expr), + ) =3D> + { + $crate::configfs_attrs!( + final: + @container($container), + @data($data), + @child($($child)?), + @no_child($($no_child)?), + @attrs($($aname $aattr)*), + @assign($($assign)*), + @cnt($cnt), + ) + }; + (final: + @container($container:ty), + @data($data:ty), + @child($($child:ty)?), + @no_child($($no_child:ident)?), + @attrs($($name:ident $attr:literal)*), + @assign($($assign:block)*), + @cnt($cnt:expr), + ) =3D> + { + $crate::macros::paste!{ + { + $( + // SAFETY: We are expanding `configfs_attrs`. + static [< $data:upper _ $name:upper _ATTR >]: + $crate::configfs::Attribute<$attr, $data, $data> = =3D + unsafe { + $crate::configfs::Attribute::new(c_str!(::= core::stringify!($name))) + }; + )* + + + // We need space for a null terminator. + const N: usize =3D $cnt + 1usize; + + // SAFETY: We are expanding `configfs_attrs`. + static [< $data:upper _ATTRS >]: + $crate::configfs::AttributeList =3D + unsafe { $crate::configfs::AttributeList::new() }; + + $($assign)* + + $( + const [<$no_child:upper>]: bool =3D true; + + static [< $data:upper _TPE >] : $crate::configfs::Item= Type<$container, $data> =3D + $crate::configfs::ItemType::<$container, $data>::n= ew::( + &THIS_MODULE, &[<$ data:upper _ATTRS >] + ); + )? + + $( + static [< $data:upper _TPE >]: + $crate::configfs::ItemType<$container, $data> =3D + $crate::configfs::ItemType::<$container, $data= >:: + new_with_child_ctor::( + &THIS_MODULE, &[<$ data:upper _ATTRS >] + ); + )? + + & [< $data:upper _TPE >] + } + } + }; + +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 496ed32b0911..ec84653ab8c7 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -40,6 +40,8 @@ pub mod block; #[doc(hidden)] pub mod build_assert; +#[cfg(CONFIG_CONFIGFS_FS)] +pub mod configfs; pub mod cred; pub mod device; pub mod device_id; --=20 2.47.0 From nobody Sat Feb 7 16:06:08 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D512122A7F6; Thu, 27 Feb 2025 12:36:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659782; cv=none; b=IxQw56GvbH+4rnY07RAr1zlWfyd55obeLDfSrSk8jrwzqDbdgG08nGhO4uN9OlnTeMScZVE0z8WyemcYo3vgZcA590wePcpMEP1Vwz3Z5B8T4+TA9PHSg0D4BoD/6uC30mK+vv9YNTAuE9ug1uCAXwsdSf6fGnwVK4/4Rk3PO3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659782; c=relaxed/simple; bh=gzrlI3NvliAeMzY2cR5gAtmOUHFIU5X+KgYZJE15a2U=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nt4LtIhCe2ZNkAkXyIzBFGy3k6dhTryWygYg7997m992lucFCZ2wti0aeLlRKat7D4Cn2slwNSisoB+wiCM/CcMnmrSs9G988rXuonwvIltUDsmt484M+OL8oObByF4260wvDNEXJFM7sUht9bosnd0O/4MMH2r2mVkPuMz1WGs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iUy8cd1M; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iUy8cd1M" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DEFF2C4CEE7; Thu, 27 Feb 2025 12:36:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740659782; bh=gzrlI3NvliAeMzY2cR5gAtmOUHFIU5X+KgYZJE15a2U=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=iUy8cd1MrSbRfzLJcWrBGqRIq1CmJMJU47eFxswPs+H/mxAh8ay2Bmh8uO02Q4UAz 32QSBKv/+HK0karLOPiDcJYgkOaZLgTAgZvoorWglxhTzyOQyoGy+VXL+Kdij7RQFX Kf4Qtb77lMz1JYue3OTfFZAuaWSBTwt6TEWlyB5PUQjxYLRxJLF4yGgKY4fihzJB+v cklMY/65l+FTgnZYN0BEArrSty1UCVUWSmpmCQZ+iywWzm/KEH3YXdO/70EKTuJ90n JizkKCTo54tcMU0F9W3lNUKaFSZCfMm5zxZgiTAXFqvgt+g7EodE12ecOtUK+lNomd pdErpMZ2PcJtA== From: Andreas Hindborg Date: Thu, 27 Feb 2025 13:35:12 +0100 Subject: [PATCH v5 3/4] rust: configfs: add a sample demonstrating configfs usage Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250227-configfs-v5-3-c40e8dc3b9cd@kernel.org> References: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> In-Reply-To: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> To: Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Joel Becker , Peter Zijlstra , Ingo Molnar , Will Deacon , Waiman Long , Fiona Behrens , Charalampos Mitrodimas , Daniel Almeida Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7360; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=gzrlI3NvliAeMzY2cR5gAtmOUHFIU5X+KgYZJE15a2U=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBnwFwENgm3R24ZkxTGWcOD/P0IMW3hqMHyLRHS0 CVMHcP6U+GJAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ8BcBAAKCRDhuBo+eShj d7BSD/4y7okTZ9NcETQJNgWB/ZAUywfauH+Grw0W6iTnYSOa/PkyWNZAyZA3IUG4yV/1nIf4bAi j0YxaOiWqqcSgoU7Jrqa9pVS/tUniTmj9p/3SQlCTq9vo+arm/ykSOffiVTRMjX98KIMaCraAuQ WLRUe9e/Rgl/Ml+pfJglK5xAj7iZTib4NqECSt/Vega34bW3+gPQwTM+CY2uWOrVpoEwXcTbc3k PFU8NqwPSdjnN4fuTWV202IAMWNSG9nTNjGVCBLUKa/T2kmA5cS5MAh82xT5WZT7W2zIN3o7o4z NgiOqoNVpGrlWQBtVR20lg33AdvzKmV+KWCCMzOvaSNtWmRSvOb+MWn3e4njgDUvBi//0CGnghM QxXqoMeUWY2cI9VnGsCjVpVMr6RoRS8Ip0L5cBRvXqaSmZYdlW/JsX9PPH6VWlTAcD1T9FQ9jt0 xqhupr2uR1RxJ93dh/Zp/k1A/qtCeJUI8yKC27nbn03NjKAT+eZ6l7TwPu9yu+/OrRbnswRd5Oj ZXt3Qnz0CAvg6z05OUMRzv9myfvaSaajVdyytOrzdfiyKNX0fV0CMFps/3U7YCc2u1aJ3jt02VR gOFzJKgIi7LdrjUQmZdG2jBQ15noyk8ImcnTATn809uXoPxtX72CuIJAijX0uKnTsf5DsNSAKkB 8YMPLzrAxH7/A4w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a sample to the samples folder, demonstrating the intended use of the Rust configfs API. Signed-off-by: Andreas Hindborg --- samples/rust/Kconfig | 11 +++ samples/rust/Makefile | 1 + samples/rust/rust_configfs.rs | 192 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 204 insertions(+) diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 918dbead2c0b..2f97bf9a7b4c 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -10,6 +10,17 @@ menuconfig SAMPLES_RUST =20 if SAMPLES_RUST =20 +config SAMPLE_RUST_CONFIGFS + tristate "Configfs sample" + depends on CONFIGFS_FS + help + This option builds the Rust configfs sample. + + To compile this as a module, choose M here: + the module will be called rust_configfs. + + If unsure, say N. + config SAMPLE_RUST_MINIMAL tristate "Minimal" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 5a8ab0df0567..72122f010caf 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) +=3D rust_misc_devic= e.o obj-$(CONFIG_SAMPLE_RUST_PRINT) +=3D rust_print.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) +=3D rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_driver_platform.o +obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) +=3D rust_configfs.o =20 rust_print-y :=3D rust_print_main.o rust_print_events.o =20 diff --git a/samples/rust/rust_configfs.rs b/samples/rust/rust_configfs.rs new file mode 100644 index 000000000000..9c0989072a8f --- /dev/null +++ b/samples/rust/rust_configfs.rs @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust configfs sample. + +use kernel::alloc::flags; +use kernel::c_str; +use kernel::configfs; +use kernel::configfs_attrs; +use kernel::new_mutex; +use kernel::page::PAGE_SIZE; +use kernel::prelude::*; +use kernel::sync::Mutex; + +module! { + type: RustConfigfs, + name: "rust_configfs", + author: "Rust for Linux Contributors", + description: "Rust configfs sample", + license: "GPL", +} + +#[pin_data] +struct RustConfigfs { + #[pin] + config: configfs::Subsystem, +} + +#[pin_data] +struct Configuration { + message: &'static CStr, + #[pin] + bar: Mutex<(KBox<[u8; PAGE_SIZE]>, usize)>, +} + +impl Configuration { + fn new() -> impl PinInit { + try_pin_init!(Self { + message: c_str!("Hello World\n"), + bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KERNEL= )?, 0)), + }) + } +} + +impl kernel::InPlaceModule for RustConfigfs { + fn init(_module: &'static ThisModule) -> impl PinInit { + pr_info!("Rust configfs sample (init)\n"); + + // Define a subsystem with the data type `Configuration`, two + // attributes, `message` and `bar` and child group type `Child`. `= mkdir` + // in the directory representing this subsystem will create direct= ories + // backed by the `Child` type. + let item_type =3D configfs_attrs! { + container: configfs::Subsystem, + data: Configuration, + child: Child, + attributes: [ + message: 0, + bar: 1, + ], + }; + + try_pin_init!(Self { + config <- configfs::Subsystem::new( + c_str!("rust_configfs"), item_type, Configuration::new() + ), + }) + } +} + +#[vtable] +impl configfs::GroupOperations for Configuration { + type Child =3D Child; + + fn make_group(&self, name: &CStr) -> Result, Error>> { + // Define a group with data type `Child`, one attribute `baz` and = child + // group type `GrandChild`. `mkdir` in the directory representing = this + // group will create directories backed by the `GrandChild` type. + let tpe =3D configfs_attrs! { + container: configfs::Group, + data: Child, + child: GrandChild, + attributes: [ + baz: 0, + ], + }; + + Ok(configfs::Group::new(name.try_into()?, tpe, Child::new())) + } +} + +#[vtable] +impl configfs::AttributeOperations<0> for Configuration { + type Data =3D Configuration; + + fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Resu= lt { + pr_info!("Show message\n"); + let data =3D container.message; + page[0..data.len()].copy_from_slice(data); + Ok(data.len()) + } +} + +#[vtable] +impl configfs::AttributeOperations<1> for Configuration { + type Data =3D Configuration; + + fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Resu= lt { + pr_info!("Show bar\n"); + let guard =3D container.bar.lock(); + let data =3D guard.0.as_slice(); + let len =3D guard.1; + page[0..len].copy_from_slice(&data[0..len]); + Ok(len) + } + + fn store(container: &Configuration, page: &[u8]) -> Result { + pr_info!("Store bar\n"); + let mut guard =3D container.bar.lock(); + guard.0[0..page.len()].copy_from_slice(page); + guard.1 =3D page.len(); + Ok(()) + } +} + +// `pin_data` cannot handle structs without braces. +#[pin_data] +struct Child {} + +impl Child { + fn new() -> impl PinInit { + try_pin_init!(Self {}) + } +} + +#[vtable] +impl configfs::GroupOperations for Child { + type Child =3D GrandChild; + + fn make_group(&self, name: &CStr) -> Result, Error>> { + // Define a group with data type `GrandChild`, one attribute `gz`.= As no + // child type is specified, it will not be possible to create subg= roups + // in this group, and `mkdir`in the directory representing this gr= oup + // will return an error. + let tpe =3D configfs_attrs! { + container: configfs::Group, + data: GrandChild, + attributes: [ + gc: 0, + ], + }; + + Ok(configfs::Group::new( + name.try_into()?, + tpe, + GrandChild::new(), + )) + } +} + +#[vtable] +impl configfs::AttributeOperations<0> for Child { + type Data =3D Child; + + fn show(_container: &Child, page: &mut [u8; PAGE_SIZE]) -> Result { + pr_info!("Show baz\n"); + let data =3D c"Hello Baz\n".to_bytes(); + page[0..data.len()].copy_from_slice(data); + Ok(data.len()) + } +} + +// `pin_data` cannot handle structs without braces. +#[pin_data] +struct GrandChild {} + +impl GrandChild { + fn new() -> impl PinInit { + try_pin_init!(Self {}) + } +} + +#[vtable] +impl configfs::AttributeOperations<0> for GrandChild { + type Data =3D GrandChild; + + fn show(_container: &GrandChild, page: &mut [u8; PAGE_SIZE]) -> Result= { + pr_info!("Show grand child\n"); + let data =3D c"Hello GC\n".to_bytes(); + page[0..data.len()].copy_from_slice(data); + Ok(data.len()) + } +} --=20 2.47.0 From nobody Sat Feb 7 16:06:08 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8100122DFFC; Thu, 27 Feb 2025 12:36:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659788; cv=none; b=tb1vMHzk0AF6h2dZkgtISk1IVUHESAUQVEvLPLjZtZU22IMRrthh2tamIaEplQW/4aLutUugGpHhmbVJc8s68dGy6+8LFzAXhJJiAZZPOngO9DjdVHsEVOFn86nWMhpWCy/ozxV4Up5uFX2Jq/oOqbhJWZcOVn+g/+sGPdL1Bhg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740659788; c=relaxed/simple; bh=+z4Qntkcydw5p8gD+O/kjP+ApAlaMKoppAU6zXIFVBI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kDHWRhW/cTneDAaxuABDK0Gvr4Ce+DO1fa7GQgG7mhyDxpcD6q+qfUAYP5C5Z/uZWiBj95BjfJepk5MQWvQVwYAq47xfKevXBG2jwpu2q/+IweYKNihiixzU/OD1FDYSv6SZOtjXBYeaqkobeF/MgINwxXhEiOEtNzlMQzFXy6U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=u6rpBAE+; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="u6rpBAE+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 089D6C4CEE6; Thu, 27 Feb 2025 12:36:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1740659788; bh=+z4Qntkcydw5p8gD+O/kjP+ApAlaMKoppAU6zXIFVBI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=u6rpBAE+2KIOrzeWcCdK4fr6JhZSPKYI0hUoMtIZJ6SSLMSCEyOTNZUh24i8sScEd aqg6+K5DUzqmytJiB7HGq6OwzLGRYU0m/NbjTgUXaMWrQmrQfTW9N7jCAtJPh2rdSH 2yRHzab+J1t+VlTF5hTPkC9TptOFwWnIi729DWnxBc2fNP3n5T4Zk+wIF7zsP+jSdx irDgyuRHuXm6bBP6Y0r+Tv1HuITvCgn51d0wIzXWw27PTSO2ljxUBAi8uRd4aZAW9a FyZkXbBH68xp2RhyUCt8UppukgysFroFhXcHqk01uIZasQHZi3/fL1tsgHsVmT48UI bi5PC/EWDxOog== From: Andreas Hindborg Date: Thu, 27 Feb 2025 13:35:13 +0100 Subject: [PATCH v5 4/4] MAINTAINERS: add entry for configfs Rust abstractions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250227-configfs-v5-4-c40e8dc3b9cd@kernel.org> References: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> In-Reply-To: <20250227-configfs-v5-0-c40e8dc3b9cd@kernel.org> To: Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Joel Becker , Peter Zijlstra , Ingo Molnar , Will Deacon , Waiman Long , Fiona Behrens , Charalampos Mitrodimas , Daniel Almeida Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=705; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=+z4Qntkcydw5p8gD+O/kjP+ApAlaMKoppAU6zXIFVBI=; b=owEBbQKS/ZANAwAIAeG4Gj55KGN3AcsmYgBnwFwEauC8FqrreRLPv1qAV5f+nYg6HKSX6Q0RA 2kv4JGf/MGJAjMEAAEIAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCZ8BcBAAKCRDhuBo+eShj d0xGD/9BDCRSxecprmFSY+apngjAyzSG2AVfbPNZ4Utme6sn722yiglxdy+XnD2jLfrttj2kEPZ RlHJx25KpdAcuNJFDTOQ5jaYHlKeIve0BSrTc/GxjPYA0Ve0Cj8JSX+WroZ5FpLv1L8PqDGx6vG eBmlDPpt7fcPZKKc0gHdWogfyHS8Npf1BN3dRE0sf+t1apldQsOtPh5tlUZJaZnC346MTHrlsli oFL1jQRGdv3fh/yrgDdNrKUIM2fok6v+B0v22+/DH6fk5RZXES8R5w/UBI+CTJ56+E4K3LeKXbs 5HDHc/QZEPH5/oteYriBDQFrgzWyCdpmsbGNvJxyrpPe6iqnwqKI54HfE6I8vssbuPcJdsQgsYe X/8cV4xjFw5bWf1+yW6ETkL5/I4L/g/9Wkdo9hCKxEqccspttXsqUvylsj9XXeRagjZhzf3Hxrb YqlEaMJmwTNif4gbm2FNtGmCxoTX96Iex0IB1cKmh+lLIfPlB3EyJjr0Bq1IgArMbfddu6jTp98 H1JIc78lDcwPDZu4SWsxtaAE38sg3NXNTPjR3lfkz6IPeYt6Ob7RJmIZYDu6iyDYRjLzgSNfBEJ +sD1aUKYdkBUdY+2EbKeo0rhdHlC9cYYB2dgNEIVk8D9mn9Zx+fSqpbHu+9CeRg2+Ooa7WPBe3H rrBLkyaMG+wecQQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Update MAINTAINERS with entry for Rust configfs abstractions. Signed-off-by: Andreas Hindborg --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 896a307fa065..9b4d5c12eb43 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5870,6 +5870,13 @@ F: fs/configfs/ F: include/linux/configfs.h F: samples/configfs/ =20 +CONFIGFS [RUST] +M: Andreas Hindborg +L: rust-for-linux@vger.kernel.org +S: Supported +F: rust/kernel/configfs.rs +F: samples/rust/rust_configfs.rs + CONGATEC BOARD CONTROLLER MFD DRIVER M: Thomas Richard S: Maintained --=20 2.47.0