From nobody Wed Oct 8 14:15:33 2025 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 C9383203706; Thu, 26 Jun 2025 20:01:05 +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=1750968065; cv=none; b=UHXmZBZuRPMhBH6pgGaPW3a6XQ1LnRUuIqgk+tcdv/hDNCxCAAx7chPIclMmVF2GeehH/a5ccG4dn6IsIDqI9T+npdxFIg+wYjp3fV5BJ5ySnnjLzQP/AjGpe/RInJ/kVFIg9kp/CKXxoE+1m+LF1b2JJuXvyk7RV7E6vq5CB4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750968065; c=relaxed/simple; bh=HcFaulFtC1Q2YuLQK6Yl9c2cSI0ryIdx5q7fmtmagns=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mgrOQO4Uz7PlNePjpQ0UGvl5uUkgjoTtMzR8vku7iNbGr+WVlq7MBVXiaJf0BJRu+7s8LbSfkPLYNdmIKXirlffnhQ2IqnWAWQ+yTm4HO8rVlCX/dO5j/FW2cqJv+AKaQIYcqpbwmb+KZnW+iUFJ6fEFAtNVg1ucCtAmjFqRjNo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=F6XB3FS2; 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="F6XB3FS2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6DB9EC4CEEF; Thu, 26 Jun 2025 20:01:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1750968065; bh=HcFaulFtC1Q2YuLQK6Yl9c2cSI0ryIdx5q7fmtmagns=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F6XB3FS2RMY1iAqbuo9dNyGQo/k+w5iLHe4q+Pq2ysSCqRUMO6uYk8e47mw1jOtmp wWdO1bdfwlZN/HDNFE3jEUp5QB3OW0Ks44Xu9KUKtodhKDzuH3kIkq24lDXR6NS5IL BX3V1KvXWtIJv4DCkcB4QKGtA8etqbOSpQR9StOwiqhU4r7MflSt8xBEg49rSxwr1f uzA7I77DIsijQcRvMHKIqtqOp+uoiSsubyRiYGadmocUZMa6HcAX5tcfbx9fI+uqWJ R9psKeLKx4CoJBKT8AM2khjTs3o5zCqfGD7y08DDCUEiQmb441TVMFSndvNmNy1YgV aaca3DbQBNbGw== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v4 1/5] rust: revocable: support fallible PinInit types Date: Thu, 26 Jun 2025 22:00:39 +0200 Message-ID: <20250626200054.243480-2-dakr@kernel.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250626200054.243480-1-dakr@kernel.org> References: <20250626200054.243480-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently, Revocable::new() only supports infallible PinInit implementations, i.e. impl PinInit. This has been sufficient so far, since users such as Devres do not support fallibility. Since this is about to change, make Revocable::new() generic over the error type E. Reviewed-by: Benno Lossin Reviewed-by: Alice Ryhl Acked-by: Miguel Ojeda Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 2 +- rust/kernel/revocable.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 8ede607414fd..fd8b75aa03bc 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -100,7 +100,7 @@ struct DevresInner { impl DevresInner { fn new(dev: &Device, data: T, flags: Flags) -> Result>> { let inner =3D Arc::pin_init( - pin_init!( DevresInner { + try_pin_init!( DevresInner { dev: dev.into(), callback: Self::devres_callback, data <- Revocable::new(data), diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs index fa1fd70efa27..46768b374656 100644 --- a/rust/kernel/revocable.rs +++ b/rust/kernel/revocable.rs @@ -82,11 +82,11 @@ unsafe impl Sync for Revocable {} =20 impl Revocable { /// Creates a new revocable instance of the given data. - pub fn new(data: impl PinInit) -> impl PinInit { - pin_init!(Self { + pub fn new(data: impl PinInit) -> impl PinInit { + try_pin_init!(Self { is_available: AtomicBool::new(true), data <- Opaque::pin_init(data), - }) + }? E) } =20 /// Tries to access the revocable wrapped object. --=20 2.49.0 From nobody Wed Oct 8 14:15:33 2025 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 BFFB127510C; Thu, 26 Jun 2025 20:01:10 +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=1750968070; cv=none; b=a+nanqtzWzAZoUz6msEZWoHaZ/pU25PbRLALbHRx2CS+263/UZzmLUnNL7oBIrMlytj36+AjIjvSwHcQwOsNOe/HZIfkgqbuxtkrxopMOXZe0yXPjm/YeC99Q1YpJNbcmqN1WrgBPI340ZW86gc6lLbfwM3004EABGQ+3SbbGeY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750968070; c=relaxed/simple; bh=1lSsD+NjV1sjQecH58pl+Un6d0nvd5+fJTKF8ToIguM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kcD1a4QxUmOgH7qLVZebk17gyaQU4V/KFP3UA4MCcvi6V97B00MLY9iptAYbIFnbz8sWesElgG9jC5OdsFSmr3hkhRjGwUzHlNOS1B/RSxAY3l/iMg2BKFj/hR3vZDvr+9H6JmgQdOcM5Pt9xijZ2xCR6bZm/NXbOcGZgIWaNvM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=MT7JGqZt; 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="MT7JGqZt" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DAA88C4CEF0; Thu, 26 Jun 2025 20:01:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1750968070; bh=1lSsD+NjV1sjQecH58pl+Un6d0nvd5+fJTKF8ToIguM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MT7JGqZtsge9NEJUY9y9EGXRgkdmosnlfkPMBvfeTIZyfIkY59GB/L1dSLbPkgA9n OZ65w7a468j+h5zJd9+NLY+oOglpCTLm8LKQkN5lLuFvFiHUQp1Medrlo135oKNZrF WnVxD7BjvZ7kbj0elS7CPh5vO58+eFA2EheKeoHhzfVsZh8+lveY8YhZnxxvmHdg7U szQdNnN0T9Qcc+SINRvi6R1gr4e/6OqPAXY9kQS8JPuxxZ3G9lZbOfTnrdJO4IKjCF c2RrxgJIxSE4+ImIn9JO4FoF6IU63uvNY0ksfrFPkiZLuE3ekbqCsn/kpV2bC1ThtJ 2POx0MHcpuORA== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich , Dave Airlie , Simona Vetter , Viresh Kumar Subject: [PATCH v4 2/5] rust: devres: replace Devres::new_foreign_owned() Date: Thu, 26 Jun 2025 22:00:40 +0200 Message-ID: <20250626200054.243480-3-dakr@kernel.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250626200054.243480-1-dakr@kernel.org> References: <20250626200054.243480-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Replace Devres::new_foreign_owned() with devres::register(). The current implementation of Devres::new_foreign_owned() creates a full Devres container instance, including the internal Revocable and completion. However, none of that is necessary for the intended use of giving full ownership of an object to devres and getting it dropped once the given device is unbound. Hence, implement devres::register(), which is limited to consume the given data, wrap it in a KBox and drop the KBox once the given device is unbound, without any other synchronization. Cc: Dave Airlie Cc: Simona Vetter Cc: Viresh Kumar Acked-by: Viresh Kumar Reviewed-by: Benno Lossin Reviewed-by: Alice Ryhl Signed-off-by: Danilo Krummrich --- rust/helpers/device.c | 7 ++++ rust/kernel/cpufreq.rs | 11 +++--- rust/kernel/devres.rs | 73 +++++++++++++++++++++++++++++++++------ rust/kernel/drm/driver.rs | 14 ++++---- 4 files changed, 85 insertions(+), 20 deletions(-) diff --git a/rust/helpers/device.c b/rust/helpers/device.c index b2135c6686b0..502fef7e9ae8 100644 --- a/rust/helpers/device.c +++ b/rust/helpers/device.c @@ -8,3 +8,10 @@ int rust_helper_devm_add_action(struct device *dev, { return devm_add_action(dev, action, data); } + +int rust_helper_devm_add_action_or_reset(struct device *dev, + void (*action)(void *), + void *data) +{ + return devm_add_action_or_reset(dev, action, data); +} diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 11b03e9d7e89..dd84e2b4d7ae 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -13,7 +13,7 @@ cpu::CpuId, cpumask, device::{Bound, Device}, - devres::Devres, + devres, error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_= DEFAULT_ERROR}, ffi::{c_char, c_ulong}, prelude::*, @@ -1046,10 +1046,13 @@ pub fn new() -> Result { =20 /// Same as [`Registration::new`], but does not return a [`Registratio= n`] instance. /// - /// Instead the [`Registration`] is owned by [`Devres`] and will be re= voked / dropped, once the + /// Instead the [`Registration`] is owned by [`devres::register`] and = will be dropped, once the /// device is detached. - pub fn new_foreign_owned(dev: &Device) -> Result { - Devres::new_foreign_owned(dev, Self::new()?, GFP_KERNEL) + pub fn new_foreign_owned(dev: &Device) -> Result + where + T: 'static, + { + devres::register(dev, Self::new()?, GFP_KERNEL) } } =20 diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index fd8b75aa03bc..64458ca3d69f 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -9,12 +9,12 @@ alloc::Flags, bindings, device::{Bound, Device}, - error::{Error, Result}, + error::{to_result, Error, Result}, ffi::c_void, prelude::*, revocable::{Revocable, RevocableGuard}, sync::{rcu, Arc, Completion}, - types::ARef, + types::{ARef, ForeignOwnable}, }; =20 #[pin_data] @@ -184,14 +184,6 @@ pub fn new(dev: &Device, data: T, flags: Flags)= -> Result { Ok(Devres(inner)) } =20 - /// Same as [`Devres::new`], but does not return a `Devres` instance. = Instead the given `data` - /// is owned by devres and will be revoked / dropped, once the device = is detached. - pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -= > Result { - let _ =3D DevresInner::new(dev, data, flags)?; - - Ok(()) - } - /// Obtain `&'a T`, bypassing the [`Revocable`]. /// /// This method allows to directly obtain a `&'a T`, bypassing the [`R= evocable`], by presenting @@ -261,3 +253,64 @@ fn drop(&mut self) { } } } + +/// Consume `data` and [`Drop::drop`] `data` once `dev` is unbound. +fn register_foreign

(dev: &Device, data: P) -> Result +where + P: ForeignOwnable + Send + 'static, +{ + let ptr =3D data.into_foreign(); + + #[allow(clippy::missing_safety_doc)] + unsafe extern "C" fn callback(ptr: *mut kernel::ffi= ::c_void) { + // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked abo= ve and hence valid. + drop(unsafe { P::from_foreign(ptr.cast()) }); + } + + // SAFETY: + // - `dev.as_raw()` is a pointer to a valid and bound device. + // - `ptr` is a valid pointer the `ForeignOwnable` devres takes owners= hip of. + to_result(unsafe { + // `devm_add_action_or_reset()` also calls `callback` on failure, = such that the + // `ForeignOwnable` is released eventually. + bindings::devm_add_action_or_reset(dev.as_raw(), Some(callback::), ptr.cast()) + }) +} + +/// Encapsulate `data` in a [`KBox`] and [`Drop::drop`] `data` once `dev` = is unbound. +/// +/// # Examples +/// +/// ```no_run +/// use kernel::{device::{Bound, Device}, devres}; +/// +/// /// Registration of e.g. a class device, IRQ, etc. +/// struct Registration; +/// +/// impl Registration { +/// fn new() -> Self { +/// // register +/// +/// Self +/// } +/// } +/// +/// impl Drop for Registration { +/// fn drop(&mut self) { +/// // unregister +/// } +/// } +/// +/// fn from_bound_context(dev: &Device) -> Result { +/// devres::register(dev, Registration::new(), GFP_KERNEL) +/// } +/// ``` +pub fn register(dev: &Device, data: impl PinInit, flags= : Flags) -> Result +where + T: Send + 'static, + Error: From, +{ + let data =3D KBox::pin_init(data, flags)?; + + register_foreign(dev, data) +} diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index acb638086131..f63addaf7235 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -5,9 +5,7 @@ //! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/dr= m_drv.h) =20 use crate::{ - bindings, device, - devres::Devres, - drm, + bindings, device, devres, drm, error::{to_result, Result}, prelude::*, str::CStr, @@ -130,18 +128,22 @@ fn new(drm: &drm::Device, flags: usize) -> Result<= Self> { } =20 /// Same as [`Registration::new`}, but transfers ownership of the [`Re= gistration`] to - /// [`Devres`]. + /// [`devres::register`]. pub fn new_foreign_owned( drm: &drm::Device, dev: &device::Device, flags: usize, - ) -> Result { + ) -> Result + where + T: 'static, + { if drm.as_ref().as_raw() !=3D dev.as_raw() { return Err(EINVAL); } =20 let reg =3D Registration::::new(drm, flags)?; - Devres::new_foreign_owned(dev, reg, GFP_KERNEL) + + devres::register(dev, reg, GFP_KERNEL) } =20 /// Returns a reference to the `Device` instance for this registration. --=20 2.49.0 From nobody Wed Oct 8 14:15:33 2025 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 3D78427510C; Thu, 26 Jun 2025 20:01:14 +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=1750968075; cv=none; b=ui0EaxOKHJDrI4zU0X52S3fJ0JhnTC/2+LFcusAotQf1Nw4Xiy/FlSYajgsz55s7MhIL8qAHjqMztO6nVrZkaSFsmjG4+9YCBiB+L6mxoDbiKFszbF0qF1DI3aZzyFYa8D/0OFlgVWZVrzbTurXN5to/t6TkrSOAT5hS9B8YfkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750968075; c=relaxed/simple; bh=CNKafnU/w8sJd21f+wejiXyHPoFd6UAL5W53BrJRbCo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jAzHDA//EriqlAEMZCAvY1lW/UxVzjmTwaJTWtEtvEC6noKsWgz5Qlc+P9b6L/E1ho6woj7DvY5MV+I6RS+XXtKxpZKOm4iRbwV9CQSAdVqxYDFjsVJO9ys9jgWjlMxwj69JREQgWbI3aVIiu2bi/H3KI2iogRiJG1j15Tywi3k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IOx4sZag; 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="IOx4sZag" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D5410C4CEEF; Thu, 26 Jun 2025 20:01:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1750968074; bh=CNKafnU/w8sJd21f+wejiXyHPoFd6UAL5W53BrJRbCo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IOx4sZagGYPpIh3/FqWsF0lcff5nMxWLMv9XgsmM4bjrqnCZxb762NFpzHQQ1UvHk hKRIf6Jkjwhwh7TxhK2Sqa8vu6m7s5jjJoV3HjXXA6Htre5ziHeqDBswq315vqu9o5 BHaCbaxk9KX1FlG21rt7UJ36SbAYHiG/zr0koqgeM148lkZHXapGGKSorM/lNLoYH6 HOOJgq9OIbC8p9wUtUZ9tufmewQ1gRdn3zvMHnXCoTfcFTJazsviVXY5dlhWokAGCV S4xQ74R0m96HrdUwf9TZPC7R+Ug+3uuAW8NvtzJW42PVThRq65wWYt2PxHw85gKo3c IlnZ5++HBJFaw== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v4 3/5] rust: devres: get rid of Devres' inner Arc Date: Thu, 26 Jun 2025 22:00:41 +0200 Message-ID: <20250626200054.243480-4-dakr@kernel.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250626200054.243480-1-dakr@kernel.org> References: <20250626200054.243480-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" So far Devres uses an inner memory allocation and reference count, i.e. an inner Arc, in order to ensure that the devres callback can't run into a use-after-free in case where the Devres object is dropped while the devres callback runs concurrently. Instead, use a completion in order to avoid a potential UAF: In Devres::drop(), if we detect that we can't remove the devres action anymore, we wait for the completion that is completed from the devres callback. If, in turn, we were able to successfully remove the devres action, we can just go ahead. This, again, allows us to get rid of the internal Arc, and instead let Devres consume an `impl PinInit` in order to return an `impl PinInit, E>`, which enables us to get away with less memory allocations. Additionally, having the resulting explicit synchronization in Devres::drop() prevents potential subtle undesired side effects of the devres callback dropping the final Arc reference asynchronously within the devres callback. Signed-off-by: Danilo Krummrich Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- drivers/gpu/nova-core/driver.rs | 7 +- drivers/gpu/nova-core/gpu.rs | 6 +- rust/kernel/devres.rs | 213 +++++++++++++++++++------------- rust/kernel/pci.rs | 20 +-- samples/rust/rust_driver_pci.rs | 19 +-- 5 files changed, 154 insertions(+), 111 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver= .rs index 8c86101c26cb..110f2b355db4 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 =20 -use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*}; +use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*, sy= nc::Arc}; =20 use crate::gpu::Gpu; =20 @@ -34,7 +34,10 @@ fn probe(pdev: &pci::Device, _info: &Self::IdInfo)= -> Result(0, c_str!("nova-c= ore/bar0"))?; + let bar =3D Arc::pin_init( + pdev.iomap_region_sized::(0, c_str!("nova-core/bar0= ")), + GFP_KERNEL, + )?; =20 let this =3D KBox::pin_init( try_pin_init!(Self { diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 60b86f370284..47653c14838b 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 =20 -use kernel::{device, devres::Devres, error::code::*, pci, prelude::*}; +use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync= ::Arc}; =20 use crate::driver::Bar0; use crate::firmware::{Firmware, FIRMWARE_VERSION}; @@ -161,14 +161,14 @@ fn new(bar: &Bar0) -> Result { pub(crate) struct Gpu { spec: Spec, /// MMIO mapping of PCI BAR 0 - bar: Devres, + bar: Arc>, fw: Firmware, } =20 impl Gpu { pub(crate) fn new( pdev: &pci::Device, - devres_bar: Devres, + devres_bar: Arc>, ) -> Result> { let bar =3D devres_bar.access(pdev.as_ref())?; let spec =3D Spec::new(bar)?; diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 64458ca3d69f..3ce8d6161778 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -13,16 +13,21 @@ ffi::c_void, prelude::*, revocable::{Revocable, RevocableGuard}, - sync::{rcu, Arc, Completion}, - types::{ARef, ForeignOwnable}, + sync::{rcu, Completion}, + types::{ARef, ForeignOwnable, Opaque, ScopeGuard}, }; =20 +use pin_init::Wrapper; + +/// [`Devres`] inner data accessed from [`Devres::callback`]. #[pin_data] -struct DevresInner { - dev: ARef, - callback: unsafe extern "C" fn(*mut c_void), +struct Inner { #[pin] data: Revocable, + /// Tracks whether [`Devres::callback`] has been completed. + #[pin] + devm: Completion, + /// Tracks whether revoking [`Self::data`] has been completed. #[pin] revoke: Completion, } @@ -44,6 +49,10 @@ struct DevresInner { /// [`Devres`] users should make sure to simply free the corresponding bac= king resource in `T`'s /// [`Drop`] implementation. /// +/// # Invariants +/// +/// [`Self::inner`] is guaranteed to be initialized and is always accessed= read-only. +/// /// # Example /// /// ```no_run @@ -88,100 +97,111 @@ struct DevresInner { /// # fn no_run(dev: &Device) -> Result<(), Error> { /// // SAFETY: Invalid usage for example purposes. /// let iomem =3D unsafe { IoMem::<{ core::mem::size_of::() }>::new(0= xBAAAAAAD)? }; -/// let devres =3D Devres::new(dev, iomem, GFP_KERNEL)?; +/// let devres =3D KBox::pin_init(Devres::new(dev, iomem), GFP_KERNEL)?; /// /// let res =3D devres.try_access().ok_or(ENXIO)?; /// res.write8(0x42, 0x0); /// # Ok(()) /// # } /// ``` -pub struct Devres(Arc>); +#[pin_data(PinnedDrop)] +pub struct Devres { + dev: ARef, + /// Pointer to [`Self::devres_callback`]. + /// + /// Has to be stored, since Rust does not guarantee to always return t= he same address for a + /// function. However, the C API uses the address as a key. + callback: unsafe extern "C" fn(*mut c_void), + /// Contains all the fields shared with [`Self::callback`]. + // TODO: Replace with `UnsafePinned`, once available. + // + // Subsequently, the `drop_in_place()` in `Devres::drop` and the expli= cit `Send` and `Sync' + // impls can be removed. + #[pin] + inner: Opaque>, +} + +impl Devres { + /// Creates a new [`Devres`] instance of the given `data`. + /// + /// The `data` encapsulated within the returned `Devres` instance' `da= ta` will be + /// (revoked)[`Revocable`] once the device is detached. + pub fn new<'a, E>( + dev: &'a Device, + data: impl PinInit + 'a, + ) -> impl PinInit + 'a + where + T: 'a, + Error: From, + { + let callback =3D Self::devres_callback; =20 -impl DevresInner { - fn new(dev: &Device, data: T, flags: Flags) -> Result>> { - let inner =3D Arc::pin_init( - try_pin_init!( DevresInner { - dev: dev.into(), - callback: Self::devres_callback, + try_pin_init!(&this in Self { + // INVARIANT: `inner` is properly initialized. + inner <- Opaque::pin_init(try_pin_init!(Inner { data <- Revocable::new(data), + devm <- Completion::new(), revoke <- Completion::new(), - }), - flags, - )?; - - // Convert `Arc` into a raw pointer and make devres o= wn this reference until - // `Self::devres_callback` is called. - let data =3D inner.clone().into_raw(); + })), + callback, + dev: { + // SAFETY: `this` is a valid pointer to uninitialized memo= ry. + let inner =3D unsafe { &raw mut (*this.as_ptr()).inner }; =20 - // SAFETY: `devm_add_action` guarantees to call `Self::devres_call= back` once `dev` is - // detached. - let ret =3D - unsafe { bindings::devm_add_action(dev.as_raw(), Some(inner.ca= llback), data as _) }; - - if ret !=3D 0 { - // SAFETY: We just created another reference to `inner` in ord= er to pass it to - // `bindings::devm_add_action`. If `bindings::devm_add_action`= fails, we have to drop - // this reference accordingly. - let _ =3D unsafe { Arc::from_raw(data) }; - return Err(Error::from_errno(ret)); - } + // SAFETY: + // - `dev.as_raw()` is a pointer to a valid bound device. + // - `inner` is guaranteed to be a valid for the duration = of the lifetime of `Self`. + // - `devm_add_action()` is guaranteed not to call `callba= ck` until `this` has been + // properly initialized, because we require `dev` (i.e.= the *bound* device) to + // live at least as long as the returned `impl PinInit<= Self, Error>`. + to_result(unsafe { + bindings::devm_add_action(dev.as_raw(), Some(callback)= , inner.cast()) + })?; =20 - Ok(inner) + dev.into() + }, + }) } =20 - fn as_ptr(&self) -> *const Self { - self as _ + fn inner(&self) -> &Inner { + // SAFETY: By the type invairants of `Self`, `inner` is properly i= nitialized and always + // accessed read-only. + unsafe { &*self.inner.get() } } =20 - fn remove_action(this: &Arc) -> bool { - // SAFETY: - // - `self.inner.dev` is a valid `Device`, - // - the `action` and `data` pointers are the exact same ones as g= iven to devm_add_action() - // previously, - // - `self` is always valid, even if the action has been released = already. - let success =3D unsafe { - bindings::devm_remove_action_nowarn( - this.dev.as_raw(), - Some(this.callback), - this.as_ptr() as _, - ) - } =3D=3D 0; - - if success { - // SAFETY: We leaked an `Arc` reference to devm_add_action() i= n `DevresInner::new`; if - // devm_remove_action_nowarn() was successful we can (and have= to) claim back ownership - // of this reference. - let _ =3D unsafe { Arc::from_raw(this.as_ptr()) }; - } - - success + fn data(&self) -> &Revocable { + &self.inner().data } =20 #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) { - let ptr =3D ptr as *mut DevresInner; - // Devres owned this memory; now that we received the callback, dr= op the `Arc` and hence the - // reference. - // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_ac= tion() in - // `DevresInner::new`. - let inner =3D unsafe { Arc::from_raw(ptr) }; + // SAFETY: In `Self::new` we've passed a valid pointer to `Inner` = to `devm_add_action()`, + // hence `ptr` must be a valid pointer to `Inner`. + let inner =3D unsafe { &*ptr.cast::>() }; + + // Ensure that `inner` can't be used anymore after we signal compl= etion of this callback. + let inner =3D ScopeGuard::new_with_data(inner, |inner| inner.devm.= complete_all()); =20 if !inner.data.revoke() { // If `revoke()` returns false, it means that `Devres::drop` a= lready started revoking - // `inner.data` for us. Hence we have to wait until `Devres::d= rop()` signals that it - // completed revoking `inner.data`. + // `data` for us. Hence we have to wait until `Devres::drop` s= ignals that it + // completed revoking `data`. inner.revoke.wait_for_completion(); } } -} =20 -impl Devres { - /// Creates a new [`Devres`] instance of the given `data`. The `data` = encapsulated within the - /// returned `Devres` instance' `data` will be revoked once the device= is detached. - pub fn new(dev: &Device, data: T, flags: Flags) -> Result= { - let inner =3D DevresInner::new(dev, data, flags)?; - - Ok(Devres(inner)) + fn remove_action(&self) -> bool { + // SAFETY: + // - `self.dev` is a valid `Device`, + // - the `action` and `data` pointers are the exact same ones as g= iven to + // `devm_add_action()` previously, + (unsafe { + bindings::devm_remove_action_nowarn( + self.dev.as_raw(), + Some(self.callback), + core::ptr::from_ref(self.inner()).cast_mut().cast(), + ) + } =3D=3D 0) } =20 /// Obtain `&'a T`, bypassing the [`Revocable`]. @@ -213,44 +233,63 @@ pub fn new(dev: &Device, data: T, flags: Flags= ) -> Result { /// } /// ``` pub fn access<'a>(&'a self, dev: &'a Device) -> Result<&'a T> { - if self.0.dev.as_raw() !=3D dev.as_raw() { + if self.dev.as_raw() !=3D dev.as_raw() { return Err(EINVAL); } =20 // SAFETY: `dev` being the same device as the device this `Devres`= has been created for - // proves that `self.0.data` hasn't been revoked and is guaranteed= to not be revoked as - // long as `dev` lives; `dev` lives at least as long as `self`. - Ok(unsafe { self.0.data.access() }) + // proves that `self.data` hasn't been revoked and is guaranteed t= o not be revoked as long + // as `dev` lives; `dev` lives at least as long as `self`. + Ok(unsafe { self.data().access() }) } =20 /// [`Devres`] accessor for [`Revocable::try_access`]. pub fn try_access(&self) -> Option> { - self.0.data.try_access() + self.data().try_access() } =20 /// [`Devres`] accessor for [`Revocable::try_access_with`]. pub fn try_access_with R>(&self, f: F) -> Option { - self.0.data.try_access_with(f) + self.data().try_access_with(f) } =20 /// [`Devres`] accessor for [`Revocable::try_access_with_guard`]. pub fn try_access_with_guard<'a>(&'a self, guard: &'a rcu::Guard) -> O= ption<&'a T> { - self.0.data.try_access_with_guard(guard) + self.data().try_access_with_guard(guard) } } =20 -impl Drop for Devres { - fn drop(&mut self) { +// SAFETY: `Devres` can be send to any task, if `T: Send`. +unsafe impl Send for Devres {} + +// SAFETY: `Devres` can be shared with any task, if `T: Sync`. +unsafe impl Sync for Devres {} + +#[pinned_drop] +impl PinnedDrop for Devres { + fn drop(self: Pin<&mut Self>) { // SAFETY: When `drop` runs, it is guaranteed that nobody is acces= sing the revocable data // anymore, hence it is safe not to wait for the grace period to f= inish. - if unsafe { self.0.data.revoke_nosync() } { - // We revoked `self.0.data` before the devres action did, henc= e try to remove it. - if !DevresInner::remove_action(&self.0) { + if unsafe { self.data().revoke_nosync() } { + // We revoked `self.data` before the devres action did, hence = try to remove it. + if !self.remove_action() { // We could not remove the devres action, which means that= it now runs concurrently, - // hence signal that `self.0.data` has been revoked succes= sfully. - self.0.revoke.complete_all(); + // hence signal that `self.data` has been revoked by us su= ccessfully. + self.inner().revoke.complete_all(); + + // Wait for `Self::devres_callback` to be done using this = object. + self.inner().devm.wait_for_completion(); } + } else { + // `Self::devres_callback` revokes `self.data` for us, hence w= ait for it to be done + // using this object. + self.inner().devm.wait_for_completion(); } + + // INVARIANT: At this point it is guaranteed that `inner` can't be= accessed any more. + // + // SAFETY: `inner` is valid for dropping. + unsafe { core::ptr::drop_in_place(self.inner.get()) }; } } =20 diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 8435f8132e38..db0eb7eaf9b1 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,7 +5,6 @@ //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) =20 use crate::{ - alloc::flags::*, bindings, container_of, device, device_id::RawDeviceId, devres::Devres, @@ -398,19 +397,20 @@ pub fn resource_len(&self, bar: u32) -> Result { impl Device { /// Mapps an entire PCI-BAR after performing a region-request on it. I= /O operation bound checks /// can be performed on compile time for offsets (plus the requested t= ype size) < SIZE. - pub fn iomap_region_sized( - &self, + pub fn iomap_region_sized<'a, const SIZE: usize>( + &'a self, bar: u32, - name: &CStr, - ) -> Result>> { - let bar =3D Bar::::new(self, bar, name)?; - let devres =3D Devres::new(self.as_ref(), bar, GFP_KERNEL)?; - - Ok(devres) + name: &'a CStr, + ) -> impl PinInit>, Error> + 'a { + Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } =20 /// Mapps an entire PCI-BAR after performing a region-request on it. - pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result> { + pub fn iomap_region<'a>( + &'a self, + bar: u32, + name: &'a CStr, + ) -> impl PinInit, Error> + 'a { self.iomap_region_sized::<0>(bar, name) } } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci= .rs index 15147e4401b2..5c35f1414172 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -25,8 +25,10 @@ impl TestIndex { const NO_EVENTFD: Self =3D Self(0); } =20 +#[pin_data(PinnedDrop)] struct SampleDriver { pdev: ARef, + #[pin] bar: Devres, } =20 @@ -73,13 +75,11 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo)= -> Result pdev.enable_device_mem()?; pdev.set_master(); =20 - let bar =3D pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("ru= st_driver_pci"))?; - - let drvdata =3D KBox::new( - Self { + let drvdata =3D KBox::pin_init( + try_pin_init!(Self { pdev: pdev.into(), - bar, - }, + bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!(= "rust_driver_pci")), + }), GFP_KERNEL, )?; =20 @@ -90,12 +90,13 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo)= -> Result Self::testdev(info, bar)? ); =20 - Ok(drvdata.into()) + Ok(drvdata) } } =20 -impl Drop for SampleDriver { - fn drop(&mut self) { +#[pinned_drop] +impl PinnedDrop for SampleDriver { + fn drop(self: Pin<&mut Self>) { dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n"); } } --=20 2.49.0 From nobody Wed Oct 8 14:15:33 2025 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 BE5E62F0E32; Thu, 26 Jun 2025 20:01:19 +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=1750968079; cv=none; b=m3YP6JRQ4gFx/e7AuyPz5AtNZsqeZKCA3QsyP2+w0AzzPsDH5x40X9+kCY62KZQDMi+oA1Wgw2dLeCH5Fbt50jXsttEM7j6MiEDdzSVuV5voVmKQ+yubIS010WgVSOL3747CxiJ4BsLnIYxPxKvPXg/x3Qz+KJnWqoMEI0SeGDE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750968079; c=relaxed/simple; bh=IwBkUuZuKXf37LiSFQBM34dVIoN+RIBZCvqe+2DZasM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l8t1QDLfkJ/ACYJhaOzCfjmWwSwbLCIsaEpIPrfO6pgG9oV+3kR0/x7yLB7L91QaHeCu9UFAwhOvEosOSXD3GYHz6IPdLR3To8Mn4pa4+46MqvURsV8lrphj2ReiTVmFSbLW0vEqakl50o1xSNUEBKUPwIM1sNr9GXlltOYdms8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Rv1356gN; 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="Rv1356gN" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4AFA3C4CEF1; Thu, 26 Jun 2025 20:01:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1750968079; bh=IwBkUuZuKXf37LiSFQBM34dVIoN+RIBZCvqe+2DZasM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Rv1356gND7vpbgjlLg24OkT1wrnzKTeomeh3ymunNL2eqqUGZXbD7k7aKWi1URiSf 5F9cmAWwanzvQ73RC539FM2QwstBkUgWgvB+6cD36U+g3t7wUcZUE7gpjrCKyRcX73 rbqJ8Y3iyiG8IryrEo6cARiH762Qa3tvzaQwBai1TiGiTnD0/bAfRRslzflvD4n28U gDd7ij2A+61NbfAO5S0EHL7SNM6sTew02r6oKpWMUIpmTaSjcRLSyrqKOLHARFlW8n TYsknfDFK71jp9I8aVu9408R/1997RXjco8vIMkvNezyjdYZG/W9TrH2U5+5rX6NnT +t1QrxWdABXSg== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v4 4/5] rust: types: ForeignOwnable: Add type Target Date: Thu, 26 Jun 2025 22:00:42 +0200 Message-ID: <20250626200054.243480-5-dakr@kernel.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250626200054.243480-1-dakr@kernel.org> References: <20250626200054.243480-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" ForeignOwnable::Target defines the payload data of a ForeignOwnable. For Arc for instance, ForeignOwnable::Target would just be T. This is useful for cases where a trait bound is required on the target type of the ForeignOwnable. For instance: fn example

(data: P) where P: ForeignOwnable, P::Target: MyTrait, {} Suggested-by: Benno Lossin Signed-off-by: Danilo Krummrich Acked-by: Miguel Ojeda Reviewed-by: Benno Lossin Reviewed-by: Boqun Feng --- rust/kernel/alloc/kbox.rs | 2 ++ rust/kernel/sync/arc.rs | 1 + rust/kernel/types.rs | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs index c386ff771d50..66fad9777567 100644 --- a/rust/kernel/alloc/kbox.rs +++ b/rust/kernel/alloc/kbox.rs @@ -403,6 +403,7 @@ unsafe impl ForeignOwnable for Box where A: Allocator, { + type Target =3D T; type PointedTo =3D T; type Borrowed<'a> =3D &'a T; type BorrowedMut<'a> =3D &'a mut T; @@ -435,6 +436,7 @@ unsafe impl ForeignOwnable for Pin> where A: Allocator, { + type Target =3D T; type PointedTo =3D T; type Borrowed<'a> =3D Pin<&'a T>; type BorrowedMut<'a> =3D Pin<&'a mut T>; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index c7af0aa48a0a..24fb63597d35 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -374,6 +374,7 @@ 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 Target =3D T; type PointedTo =3D ArcInner; type Borrowed<'a> =3D ArcBorrow<'a, T>; type BorrowedMut<'a> =3D Self::Borrowed<'a>; diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 3958a5f44d56..74c787b352a9 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -27,6 +27,9 @@ /// [`into_foreign`]: Self::into_foreign /// [`PointedTo`]: Self::PointedTo pub unsafe trait ForeignOwnable: Sized { + /// The payload type of the foreign-owned value. + type Target; + /// Type used when the value is foreign-owned. In practical terms only= defines the alignment of /// the pointer. type PointedTo; @@ -128,6 +131,7 @@ unsafe fn try_from_foreign(ptr: *mut Self::PointedTo) -= > Option { =20 // SAFETY: The `into_foreign` function returns a pointer that is dangling,= but well-aligned. unsafe impl ForeignOwnable for () { + type Target =3D (); type PointedTo =3D (); type Borrowed<'a> =3D (); type BorrowedMut<'a> =3D (); --=20 2.49.0 From nobody Wed Oct 8 14:15:33 2025 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 2B5912F0E4D; Thu, 26 Jun 2025 20:01:23 +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=1750968084; cv=none; b=T8QD6UDq2JCsJs7buvSvIQENv7/LQLleecFBe6YRqe7eaQBqErAaMUKOJsJRbsj1w8eIWDl9RzHBffZmiSHkgQgFd0CurcQ2bhhz0IHD/n8h1U90nHvie2kHtXZJq44pmZpmLwTFBFb383gdKcNpU3D2qTDxE+uXcwaJ009Yt6o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750968084; c=relaxed/simple; bh=4TQ9IPTgKMt3xedIjC4FAr1Kkj8BMJZaabqz8iQUEgs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OTvzWe1Cl3UuUxwmBiX5iR9C01onTBx9zk0usXx8DHtUR9gEhCZ6FtlrO5Ue8JORuorBNO1wg9t5Lg5j1rpW9r7vScvyd2AffX9CI+xbhsqbklskUOuOj7uYvS5fz2crXVGljrl7UQ5qMGQvZcM8IZCyBVZXzxNezJMnMmVn2HE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ko6VVeNj; 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="Ko6VVeNj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B4973C4CEEB; Thu, 26 Jun 2025 20:01:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1750968083; bh=4TQ9IPTgKMt3xedIjC4FAr1Kkj8BMJZaabqz8iQUEgs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ko6VVeNjjltedZWWEr9JHf3QkUmh01r1yUn9E3XF+Li18su0PKFM+PqTpgghPLqpz CxhzeRJwLv3AEpk8nZKwLOl/rVw0YWvqrJ/sy9IUb/P2TfWuB0Rd+we08iX7EZQvCI BNEjsZ+VSwIYdPK8K+ndN+CgdwCxsFeisHTm9AATI1svKMl+Bs1lw7Z5qNIlxZp17e sLl/YrZRGw9mlhOzFAFFYqEosQrVr4BgP/RUYquAbeygkuoDxfcrK3RD4Dw3tQxeBS swjl1S4UDvZx5w07eeF01ICNsr6qVfd1mQSmuflP3CChBlXIVFQnt/fybWZV2RvkdV srfxv/LtrmdRA== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org, kwilczynski@kernel.org, bhelgaas@google.com Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Danilo Krummrich Subject: [PATCH v4 5/5] rust: devres: implement register_release() Date: Thu, 26 Jun 2025 22:00:43 +0200 Message-ID: <20250626200054.243480-6-dakr@kernel.org> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250626200054.243480-1-dakr@kernel.org> References: <20250626200054.243480-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" register_release() is useful when a device resource has associated data, but does not require the capability of accessing it or manually releasing it. If we would want to be able to access the device resource and release the device resource manually before the device is unbound, but still keep access to the associated data, we could implement it as follows. struct Registration { inner: Devres, data: T, } However, if we never need to access the resource or release it manually, register_release() is great optimization for the above, since it does not require the synchronization of the Devres type. Suggested-by: Alice Ryhl Signed-off-by: Danilo Krummrich Reviewed-by: Boqun Feng --- rust/kernel/devres.rs | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 3ce8d6161778..92aca78874ff 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -353,3 +353,76 @@ pub fn register(dev: &Device, data: impl = PinInit, flags: Flag =20 register_foreign(dev, data) } + +/// [`Devres`]-releaseable resource. +/// +/// Register an object implementing this trait with [`register_release`]. = Its `release` +/// function will be called once the device is being unbound. +pub trait Release { + /// The [`ForeignOwnable`] pointer type consumed by [`register_release= `]. + type Ptr: ForeignOwnable; + + /// Called once the [`Device`] given to [`register_release`] is unboun= d. + fn release(this: Self::Ptr); +} + +/// Consume the `data`, [`Release::release`] and [`Drop::drop`] `data` onc= e `dev` is unbound. +/// +/// # Examples +/// +/// ```no_run +/// use kernel::{device::{Bound, Device}, devres, devres::Release, sync::A= rc}; +/// +/// /// Registration of e.g. a class device, IRQ, etc. +/// struct Registration; +/// +/// impl Registration { +/// fn new() -> Result> { +/// // register +/// +/// Ok(Arc::new(Self, GFP_KERNEL)?) +/// } +/// } +/// +/// impl Release for Registration { +/// type Ptr =3D Arc; +/// +/// fn release(this: Arc) { +/// // unregister +/// } +/// } +/// +/// fn from_bound_context(dev: &Device) -> Result { +/// let reg =3D Registration::new()?; +/// +/// devres::register_release(dev, reg.clone()) +/// } +/// ``` +pub fn register_release

(dev: &Device, data: P) -> Result +where + P: ForeignOwnable, + P::Target: Release + Send, +{ + let ptr =3D data.into_foreign(); + + #[allow(clippy::missing_safety_doc)] + unsafe extern "C" fn callback

(ptr: *mut kernel::ffi::c_void) + where + P: ForeignOwnable, + P::Target: Release, + { + // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked abo= ve and hence valid. + let data =3D unsafe { P::from_foreign(ptr.cast()) }; + + P::Target::release(data); + } + + // SAFETY: + // - `dev.as_raw()` is a pointer to a valid and bound device. + // - `ptr` is a valid pointer the `ForeignOwnable` devres takes owners= hip of. + to_result(unsafe { + // `devm_add_action_or_reset()` also calls `callback` on failure, = such that the + // `ForeignOwnable` is released eventually. + bindings::devm_add_action_or_reset(dev.as_raw(), Some(callback::), ptr.cast()) + }) +} --=20 2.49.0