From nobody Fri Jul 24 22:54:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 1B6BD35C6A0; Wed, 22 Jul 2026 14:50:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731825; cv=none; b=cB6M9EYWUVyOzsvnNpoCoNxfTnOeEBpjL+0ZHVW4UuKyQxBU04BZf/fvy98U6e1GpwoOYBnUiVr8xORkX3ipwSx8EQBog/rqI2olBoqxHRhf7fC/Loml7Iq8uvCzWAVHoZLJi5DXBm4vGqBGI+TnQo+Kg9LI00mWph1IrUTziFM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731825; c=relaxed/simple; bh=TGSVS27VsGgTdKeWuqJIXGATo3lWZJfA/ki8bAvqyz8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l6a0MjTQ4N3NljUBfIYc6NwrPjE/GU24i3FXlopp75VvIr/TJMAC/y53qaUK3sA29TdnzaV0SGws2QeVldceJ0xM1mcT/iFLW/JjThBp0JIhklBF1HkrDJuKcJdHyzNHir4d3ltWNEj8QIFoG7npqFfJOExuyFlIpN9sRiEeDVs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CdyiLiy6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CdyiLiy6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DC3DA1F00A3A; Wed, 22 Jul 2026 14:50:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784731823; bh=LXJwHWfyIyYAol7hr0XIHN7RcUAOgHD73uS/AqOnnf4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=CdyiLiy6QQZFkwdpmQspAU4qGFtRk7dLsGHJnIWbp+o8I7nYrzv7M2bePkHhawT64 TXu5Yc54zp3Dg14JUPZ24EEYv8Gd2QnPertb6B/ey6Yefq6O2DHn/87SrGdmI63kz+ NqSxBBWeIhr+JEfKI6fTfbTz85t9DeHXbQsdWafUwzBCLdsV6unGA0phw0T7t5UraA B+KxuPlQcl08Xup7R6myhFpRTBSzxCnPgHPFiPDHnmXddysDKLWmlrylAnDpEPfCK4 nF3/+fCDFC+TzoGQpBfCnNnzdemVAg5VA3LVpHjrA4x3djK0BQcSTxgzhC8C5tUSM7 ppKTcFAVsU5ww== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?UTF-8?q?Onur=20=C3=96zkan?= , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , Lyude Paul , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Greg Kroah-Hartman , "Yury Norov (NVIDIA)" , Asahi Lina , Lorenzo Stoakes , FUJITA Tomonori , Eliot Courtney , Mirko Adzic , Daniel del Castillo , Alistair Francis Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH v6 1/6] rust: types: implement ForeignOwnable for ARef Date: Wed, 22 Jul 2026 16:49:25 +0200 Message-ID: <20260722144931.1070312-3-phasta@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260722144931.1070312-2-phasta@kernel.org> References: <20260722144931.1070312-2-phasta@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" From: Danilo Krummrich Implement ForeignOwnable for ARef, making it possible for C code to own an ARef. Since ARef represents shared ownership, BorrowedMut is &T rather than &mut T, matching the semantics of the underlying reference-counted type. Signed-off-by: Danilo Krummrich Reviewed-by: Alice Ryhl Tested-by: Daniel Almeida --- rust/kernel/sync/aref.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs index b721b2e00b98..540766613659 100644 --- a/rust/kernel/sync/aref.rs +++ b/rust/kernel/sync/aref.rs @@ -24,6 +24,11 @@ ptr::NonNull, // }; =20 +use crate::{ + prelude::*, + types::ForeignOwnable, // +}; + /// Types that are _always_ reference counted. /// /// It allows such types to define their own custom ref increment and decr= ement functions. @@ -188,6 +193,41 @@ fn eq(&self, other: &ARef) -> bool { } impl Eq for ARef {} =20 +// SAFETY: `into_foreign` returns a pointer from `NonNull::as_ptr`, so it'= s non-null. The +// `ARef` invariant guarantees that `ptr` points to a valid `T`, so it's a= ligned to `T`. +unsafe impl ForeignOwnable for ARef { + const FOREIGN_ALIGN: usize =3D core::mem::align_of::(); + + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'a T; + + fn into_foreign(self) -> *mut c_void { + ARef::into_raw(self).as_ptr().cast() + } + + unsafe fn from_foreign(ptr: *mut c_void) -> Self { + // SAFETY: The safety requirements of this function ensure that `p= tr` comes from a previous + // call to `Self::into_foreign`. + let ptr =3D unsafe { NonNull::new_unchecked(ptr.cast()) }; + + // SAFETY: `ptr` came from `into_foreign`, which consumed an `ARef= ` without decrementing + // the refcount, so we can transfer the ownership to the new `ARef= `. + unsafe { ARef::from_raw(ptr) } + } + + unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: The safety requirements of this method ensure that the = object remains alive and + // immutable for the duration of 'a. + unsafe { &*ptr.cast() } + } + + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: The safety requirements for `borrow_mut` are a superset= of the safety + // requirements for `borrow`. + unsafe { ::borrow(ptr) } + } +} + impl PartialEq<&'_ U> for ARef where T: AlwaysRefCounted + PartialEq, --=20 2.55.0 From nobody Fri Jul 24 22:54:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 562082475CF; Wed, 22 Jul 2026 14:50:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731834; cv=none; b=KG7XTbQNTu7D/wX4CoaVP//pYRHZ8vc917wCVBn1NskiEwK3jj9whG1BeH7QQA3whlck2Z0PhmBSzlamu2+kgT11G9qc528xticH1Ao+mgAKB+iuZBA/hQ7K3+/FMpgX2qW1s/Q0nwZRrXrJ28CfowtyF/oJcy2+ux3OCOSgcUE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731834; c=relaxed/simple; bh=aSMYU3rAG5+Gh8QRe3AZRYYIPcLUqtmNgzBxP4IU/2k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=drV6smI2kZ5cMT0hFU+0VKuoZn6vYh6fifx8bI8kISNvjS2urKWC8tqLZDalvStItQEQri0BMAaDybLHKF6XOvQebHYnuwoQPnmuFywLzBqQtRQ5fOiK2MsifAmCvzrkznU+BjaDmHOdDsXL1zwCZseLW7RzWudHLoREDP28YLQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QKtKATec; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QKtKATec" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1CF311F000E9; Wed, 22 Jul 2026 14:50:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784731833; bh=DZ3kMcQI0qYmxVkBtHIsi69cXvHkfXdSOkp6hTp/YSU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=QKtKATecshukMber2zGZmeYjySeN8q5+zEMXImK5x2izgsyFTYB6iEQBqUGP8RB9I uzpTB54JbyVugpyz9YiW5UjINIximmFRzrK0Hb9zkTt66ZR0t1f/l2kL/cE9uzuDIj 6sQy3ImqEjDdQUhc9BGCCgze+n++dx9U5otkWgDs0UF/le6DgHR180ZW1BM9Y1I6dL IYhAU3fo5i6eW0YT3+7OENWJWL5l5zLCmqgYqtF2CJYlDc+DDONLbe6ImsSRHpA8EK bQU0wFB6Ye8TVBefeR/9814xfOcJvsuNtS/1fV6LDNgbkVGhFmLwp/19PdcAYGGyMq cI8s7pYpMfK7Q== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?UTF-8?q?Onur=20=C3=96zkan?= , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , Lyude Paul , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Greg Kroah-Hartman , "Yury Norov (NVIDIA)" , Asahi Lina , Lorenzo Stoakes , FUJITA Tomonori , Eliot Courtney , Mirko Adzic , Daniel del Castillo , Alistair Francis Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH v6 2/6] rust: error: Add ECANCELED error code Date: Wed, 22 Jul 2026 16:49:26 +0200 Message-ID: <20260722144931.1070312-4-phasta@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260722144931.1070312-2-phasta@kernel.org> References: <20260722144931.1070312-2-phasta@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" The Rust dma_fence abstractions need the ECANCELED error code. Add ECANCELED error code. Signed-off-by: Philipp Stanner Tested-by: Daniel Almeida Reviewed-by: Alistair Francis --- rust/kernel/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index a56ba6309594..cb666f0d42ee 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -67,6 +67,7 @@ macro_rules! declare_err { declare_err!(EOVERFLOW, "Value too large for defined data type."); declare_err!(EMSGSIZE, "Message too long."); declare_err!(ETIMEDOUT, "Connection timed out."); + declare_err!(ECANCELED, "Request has been canceled."); declare_err!(ERESTARTSYS, "Restart the system call."); declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal = and will be restarted."); declare_err!(ERESTARTNOHAND, "Restart if no handler."); --=20 2.55.0 From nobody Fri Jul 24 22:54:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 321364207A; Wed, 22 Jul 2026 14:50:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731843; cv=none; b=WnfHpBMThHm2Jopl2mathj3Uk2qYeiyz0ijMWViACe4EvKjt3Dq1Wie2DJ2CTT/LV87coXDJC/Z26JzNzBL9s5S7j+sCdyA7VBMyH+9IpQaFU68ga2pgycVHYZWe6ljxPoWzIiZZjNIiTjKGCv+j7iTpG+u/PBVYh316pyz5aZ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731843; c=relaxed/simple; bh=YOlv3o40GyVVy99CqrmV5kA8RFAg585fD7edIGWhAR8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=phQQe6LmxQTVwjp55PhyDykU/ofPNO8HbXT2ojnmsfhKcsBCeUYbt6Ys3kkbUWnAtqw3OcCbMwDaq6FC4HdyTieEqDmpsUSDpnvO7peNq6Q4jb8YcLDNDTqQVsV9Ugi64wk6PtIEI75S9aS1s4Bht8UWR2Cvqk0ortaV57pkWEU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=O3agtlz8; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="O3agtlz8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6A8C81F00A3A; Wed, 22 Jul 2026 14:50:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784731842; bh=ES4LoSb21zgaO4ykRP24VmWuxpdtjPaJPxGZBbKvNvM=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=O3agtlz8CQeyLb3XO8RLdesTiSoSFh2BEKmvCXpK3PlTnMUXBfPzDZNKUxMFp5nR3 qBBHHS3PHkh2Qb1o8a/I61lgkZcVQi0ScUfqlamCX4vaVAW3gluRhBraJklX/qDp9H iLvTypX2eD3f7IY8m5012YlakQ79oMDVkBGxF0uG1PMzbOuF2hRjY8bwQBnWfQp+U7 26IYY2DuxQuXRHYZHmkfMWq8jDB1HrxpDIXyAaXisQ8CVHmPRsBj2C9hKT2KZS5en6 0Vfj/eRQvMvZWf+xYirXDUFc4xSlE7Q7dn48oOD2ypszoIr3/+yBOin6FynmEc3UTA wkwoh5UYK6HfA== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?UTF-8?q?Onur=20=C3=96zkan?= , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , Lyude Paul , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Greg Kroah-Hartman , "Yury Norov (NVIDIA)" , Asahi Lina , Lorenzo Stoakes , FUJITA Tomonori , Eliot Courtney , Mirko Adzic , Daniel del Castillo , Alistair Francis Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH v6 3/6] rust: sync: Add abstraction for rcu_barrier() Date: Wed, 22 Jul 2026 16:49:27 +0200 Message-ID: <20260722144931.1070312-5-phasta@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260722144931.1070312-2-phasta@kernel.org> References: <20260722144931.1070312-2-phasta@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" rcu_barrier() is a frequently used C function which is always safe to be called. Add a safe abstraction for rcu_barrier(). Signed-off-by: Philipp Stanner Tested-by: Daniel Almeida --- rust/kernel/sync/rcu.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs index a32bef6e490b..7031ca5d2473 100644 --- a/rust/kernel/sync/rcu.rs +++ b/rust/kernel/sync/rcu.rs @@ -50,3 +50,23 @@ fn drop(&mut self) { pub fn read_lock() -> Guard { Guard::new() } + +/// Wait until all in-flight call_rcu() callbacks complete. +/// +/// Note that this primitive does not necessarily wait for an RCU grace pe= riod +/// to complete. For example, if there are no RCU callbacks queued anywhe= re +/// in the system, then rcu_barrier() is within its rights to return +/// immediately, without waiting for anything, much less an RCU grace peri= od. +/// In fact, rcu_barrier() will normally not result in any RCU grace perio= ds +/// beyond those that were already destined to be executed. +/// +/// In kernels built with CONFIG_RCU_LAZY=3Dy, this function also hurries = all +/// pending lazy RCU callbacks. +/// +/// Note that this is one of the RCU primitives which must not be called in +/// atomic context. +#[inline] +pub fn rcu_barrier() { + // SAFETY: `rcu_barrier()` is always safe to be called. It just might = wait for a grace period. + unsafe { bindings::rcu_barrier() }; +} --=20 2.55.0 From nobody Fri Jul 24 22:54:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 A0EF640F73C; Wed, 22 Jul 2026 14:50:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731852; cv=none; b=Rsb+gBszuemI+oxnZ0+kF8kLoTzjvIpcdRu+4c96gB8XElHcqJDZEIS0VmHALv0AihtKXvusDll9fR5U+yoHOUym+6cwh+CKX9Ij8i/392PrPmG/V3ytbALm1gmJM4S82gBNgFy88IZVtAt1Kdfg8heivPGm4nXKCeBxqMIfzfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731852; c=relaxed/simple; bh=qqCRQvhVH42yNHmiFbVEtFeDDBt7lPvhjqlxqtQQK+k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XucpdaHWgF9i1lF+4J60GBybpL4KhitoaL4PyDDytN6LpI9l0AMSSOkRomlmzCKDVDwiCvkxvLg8QJ9YRIiqEIzgXcifMPIJ7uO79BI0e7SCxffJ5CjwJvmL5nYsniIoUU2k+Y/8rHYF41zDBuzRbN36+49n4ztHMMwS8RKOmXI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xvgdbxnb; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xvgdbxnb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7E20E1F000E9; Wed, 22 Jul 2026 14:50:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784731851; bh=fb3KwzqW2X+XJDIN9Q4LM2WNavmSoabSD1bIH2UFAQ4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=XvgdbxnbthVpPVWwYzLXKcoKXAB2cU0AuqZ0xF6VD+EDUspJ9nXxYMdxdrJgv4i4H qRODWJQ+wBL7D71ZvpvbzRGFBAtdBpW5UWdgVSFWHMyQbGBe3xFVB36kgaRUASABKo wetA0iX35N0v6G+oqeV7oiuyLJOuHrdarSR+YQC6zbnNU2ktvYcM38LSDieJKBYt2e FbJb34si8Jtjk8eZffouDqe7USTWvTfL3I4go5VNqb9wq4oYzgfcdJBMNrm+39y7WE W+DMV3d7TJEhVGNBWCXsQ0EPT0WjOFnej+64PSrMxCWSdwWzxaTt1oA+DnWJWzqDQL Va5MYNekDdw6w== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?UTF-8?q?Onur=20=C3=96zkan?= , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , Lyude Paul , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Greg Kroah-Hartman , "Yury Norov (NVIDIA)" , Asahi Lina , Lorenzo Stoakes , FUJITA Tomonori , Eliot Courtney , Mirko Adzic , Daniel del Castillo , Alistair Francis Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH v6 4/6] rust: xarray: Add support for index reservations Date: Wed, 22 Jul 2026 16:49:28 +0200 Message-ID: <20260722144931.1070312-6-phasta@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260722144931.1070312-2-phasta@kernel.org> References: <20260722144931.1070312-2-phasta@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" One often cannot allocate in the kernel with the desired flags, most notably in atomic context. Pre-allocating the memory is the preferred solution in such situations. Add support for xa_reserve() in the Rust abstractions of xarray. Create a Reservation object similar to a lock-guard, that can be dropped once the reservation is no longer needed or once the index was stored to. Signed-off-by: Philipp Stanner --- Please regard this more as an RFC. I need pre-allocating in XArray for DmaFence. How exactly we achieve this is open for discussion. P. --- rust/helpers/xarray.c | 10 ++++++++++ rust/kernel/xarray.rs | 45 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c index 08979b304341..1504ab242949 100644 --- a/rust/helpers/xarray.c +++ b/rust/helpers/xarray.c @@ -26,3 +26,13 @@ __rust_helper void rust_helper_xa_unlock(struct xarray *= xa) { return xa_unlock(xa); } + +__rust_helper int rust_helper_xa_reserve(struct xarray *xa, unsigned long = index, gfp_t flags) +{ + return xa_reserve(xa, index, flags); +} + +__rust_helper void rust_helper_xa_release(struct xarray *xa, unsigned long= index) +{ + xa_release(xa, index); +} diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 987c9c0c2198..fa5d47935f0a 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -8,7 +8,10 @@ alloc, bindings, build_assert::build_assert, - error::{Error, Result}, + error::{ + Error, + Result, // + }, ffi::c_void, types::{ ForeignOwnable, @@ -147,6 +150,46 @@ pub fn lock(&self) -> Guard<'_, T> { _not_send: NotThreadSafe, } } + + /// Reserve memory in the XArray at the appropriate index. If this call + /// succeeds, later storing at the index will not require an allocatio= n. + /// + /// Loading from reserved entries will return `None`. + pub fn reserve(&self, index: usize, gfp: alloc::Flags) -> Result> { + // SAFETY: + // - `self.xa.xa` is always valid by the type invariant. + // - The caller holds the lock. + // + // INVARIANT: `new` came from `T::into_foreign`. + let ret =3D unsafe { bindings::xa_reserve(self.xa.get(), index, gf= p.as_raw()) }; + match ret { + 0 =3D> Ok(Reservation { xa: self, index }), + _ =3D> Err(Error::from_errno(ret)), + } + } +} + +/// An index reservation object. +/// +/// As long as you keep this object alive, your reservation will stay vali= d. If +/// someone has stored to this index in the meantime, dropping your reserv= ation +/// does nothing, which is why you can always drop this reservation object= once +/// you performed your store operation. +/// +/// Refer to the main C xarray documentation for more details. +pub struct Reservation<'a, T: ForeignOwnable> { + xa: &'a XArray, + index: usize, +} + +impl Drop for Reservation<'_, T> { + fn drop(&mut self) { + // SAFETY: + // - `self.xa` is always valid by the type invariant. + // - If `self.id` is not used or has been stored to by a racing pa= rty, + // `xa_release()` will do nothing. + unsafe { bindings::xa_release(self.xa.xa.get(), self.index) }; + } } =20 /// A lock guard. --=20 2.55.0 From nobody Fri Jul 24 22:54:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 1F77833BBAD; Wed, 22 Jul 2026 14:51:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731864; cv=none; b=ocMEwUJQB0hXiwz2jxLBEuOcoT1b4iqut+RcJoJOFHDWQulSrGqaTxWvacTHOKLcUlDbtkc9CAR6uZYXXvD051rd52Ai9ncfyIGggpyYmUg3is+gKvqu0EIssuaJfY7LoJxjhSQYJQR69CBWFX956Er7+IalMgqK3UmCC+i4MRc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731864; c=relaxed/simple; bh=i48UsU4HQ8B14I3ENFjSBVDJ2hPy1TPIUTYpA2FRsHs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jImbIVgVUnvQ64DvS/Zhr6iHjzApadmQIL3cBCBPutDSDLI2bcmaqddFH8gQ9Udvl73hnfYzM+W9JFan3r4TCYkz0UMOTNN8t48mFkKsRPNXt+MHokSoEVQuhOmA/VCwGJEj0MNtaTp742DwpYDPWxqeRr/RLoctj3nFVCP/p7w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KicZtDof; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KicZtDof" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BB66A1F00A3A; Wed, 22 Jul 2026 14:50:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784731860; bh=Cz0b9y5Y5YDhuxkhvORqPPq0VjStHjt9qasOz4gMo+0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KicZtDof3fmvvJKy7m1Rh1rC1KUAdjVLN0Fl+YcI+GVhaNc76s0zaFvQbDbQcmwak mug4JNIP18kvAPdVXkhbd1bP8Q1qMQLuXSHP988Kvn+HJmE5/nKy0Rug7m1G0Dz5s3 4KcgcKMEV8Z2Hsubmb9TR2jjhez+rN2L3MKY/0T36Vb+GkmO0FQniQdTth6I2qGieb R7eOHNEAcTm1uE2y6KRRlj97Yevx5u+mFBzOT5g+MWVyi3ifGB2oseLGvd55dlXyCK YAPj52IeKtUL+vFKfuOIlL4zjwJahhKoKfFpy3n3p6QwOgEYLUMjWuSN3TmlzWNVGQ kqSft14oPAsyw== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?UTF-8?q?Onur=20=C3=96zkan?= , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , Lyude Paul , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Greg Kroah-Hartman , "Yury Norov (NVIDIA)" , Asahi Lina , Lorenzo Stoakes , FUJITA Tomonori , Eliot Courtney , Mirko Adzic , Daniel del Castillo , Alistair Francis Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH v6 5/6] rust: Add dma_fence abstractions Date: Wed, 22 Jul 2026 16:49:29 +0200 Message-ID: <20260722144931.1070312-7-phasta@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260722144931.1070312-2-phasta@kernel.org> References: <20260722144931.1070312-2-phasta@kernel.org> 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 C's dma_fence's are synchronisation primitives that will be needed by all Rust GPU drivers. The dma_fence framework sets a number of rules, notably: - fences must only be signaled once - all fences must be signaled at some point - fence error codes must only be set before signaling - every pointer to a fence must be backed by a reference All those rules are being addressed by these abstractions. To cleanly decouple fence issuers and consumers, two types are provided: - DriverFence: the only fence type that can be signaled and that carries driver-specific data. - Fence: the fence type to be shared with other drivers and / or userspace. The only type callbacks can be registered on. Cannot be signaled. Hereby, a Fence lives in the same chunk of memory as a DriverFence. Both share the refcount of the underlying C dma_fence. Since this implementation does not provide a custom dma_fence_backend_ops.release() function, the memory is freed by the dma_fence backend once the refcount drops to 0. To create a DriverFence, the user must first allocate a DriverFenceAllocation, so that the creation of the DriverFence later on can always succeed. Otherwise, deadlocks could occur if fences need to be created in a GPU job submission path. Synchronization is ensured by the dma_fence backend. All DriverFence's created through this abstraction must be signaled by the creator with an error code. In case a DriverFence drops without being signaled beforehand, it is signaled with -ECANCELLED as its error and a warning is printed. This allows the Rust abstraction to very cleanly decouple fence issuer and consumer by relying on the decoupling mechanisms in the C backend, which ensures through RCU and the 'signaled' fence-flag that dma_fence_backend_ops functions cannot access the potentially unloaded driver code anymore. Signalling fences on drop thus grants many advantages. Not signaling fences on drop would risk deadlock and does not grant real advantages: By definition only the drivers can ensure that a fence always represents the hardware's state correctly. This implementation models a DmaFenceContext object on which fences are to be created, thereby ensuring correct sequence numbering according to the timeline. dma_fence supports a variety of callbacks. The mandatory callbacks (get_timeline_name() and get_driver_name()) are implemented in this patch. For convenience, they store those name parameters in the fence context, saving the driver from implementing these two callbacks. Support for other callbacks (like for hardware signaling) is prepared for through the fact that both DriverFence and Fence live in the same allocation, allowing for usage of container_of from the callback to access the driver-specific data. It is expected that other callbacks, added in the future, also mostly operate on the generic data in the FenceContext. To make this safe, the implementation ensures through a lifetime that a DriverFence cannot outlive its FenceContext. Since this can, currently, not be fully guaranteed (core::mem::forget() could make the compiler forget about the memory) in Rust, an additional safety measure is implemented: the FenceContext keeps track of all unsignaled fences, and should it ever drop with such a fence present, it will signal it, ensuring full decoupling. Synchronization for dma_fence_ops callbacks is ensured by only running the Rust deconstructor delayed with call_rcu(), which prevents UAF-bugs should a DriverFence drop while a Fence callback is currently operating on the associated driver data. Since they can also operate on the FenceContext's data, its drop implementation also performs the necessary delay with rcu_barrier(). Add abstractions for dma_fence in Rust. Signed-off-by: Philipp Stanner --- rust/bindings/bindings_helper.h | 1 + rust/helpers/dma_fence.c | 48 ++ rust/helpers/helpers.c | 1 + rust/kernel/dma_buf/dma_fence.rs | 1079 ++++++++++++++++++++++++++++++ rust/kernel/dma_buf/mod.rs | 14 + rust/kernel/lib.rs | 1 + 6 files changed, 1144 insertions(+) create mode 100644 rust/helpers/dma_fence.c create mode 100644 rust/kernel/dma_buf/dma_fence.rs create mode 100644 rust/kernel/dma_buf/mod.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 1124785e210b..54b62d952e01 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/dma_fence.c b/rust/helpers/dma_fence.c new file mode 100644 index 000000000000..0e08411098fa --- /dev/null +++ b/rust/helpers/dma_fence.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +__rust_helper void rust_helper_dma_fence_get(struct dma_fence *f) +{ + dma_fence_get(f); +} + +__rust_helper void rust_helper_dma_fence_put(struct dma_fence *f) +{ + dma_fence_put(f); +} + +__rust_helper bool rust_helper_dma_fence_begin_signalling(void) +{ + return dma_fence_begin_signalling(); +} + +__rust_helper void rust_helper_dma_fence_end_signalling(bool cookie) +{ + dma_fence_end_signalling(cookie); +} + +__rust_helper bool rust_helper_dma_fence_is_signaled(struct dma_fence *f) +{ + return dma_fence_is_signaled(f); +} + +__rust_helper bool rust_helper_dma_fence_test_signaled_flag(struct dma_fen= ce *f) +{ + return dma_fence_test_signaled_flag(f); +} + +__rust_helper void rust_helper_dma_fence_lock_irqsave(struct dma_fence *f,= unsigned long *flags) +{ + dma_fence_lock_irqsave(f, *flags); +} + +__rust_helper void rust_helper_dma_fence_unlock_irqrestore(struct dma_fenc= e *f, unsigned long *flags) +{ + dma_fence_unlock_irqrestore(f, *flags); +} + +__rust_helper void rust_helper_dma_fence_set_error(struct dma_fence *f, in= t error) +{ + dma_fence_set_error(f, error); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 998e31052e66..4ab8aa9da7e7 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -58,6 +58,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma_fence.c" #include "dma-resv.c" #include "drm.c" #include "drm_gpuvm.c" diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fen= ce.rs new file mode 100644 index 000000000000..a7d9e22e7e81 --- /dev/null +++ b/rust/kernel/dma_buf/dma_fence.rs @@ -0,0 +1,1079 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2025, 2026 Red Hat Inc.: + * Author: Philipp Stanner + */ + +//! DriverFence support. +//! +//! Reference: +//! +//! header: [`include/linux/dma-fence.h`](srctree/include/linux/dma-fence.= h) + +use crate::{ + alloc::AllocError, + bindings, + container_of, + error::to_result, + pr_warn_once, + prelude::*, + types::ForeignOwnable, + types::Opaque, + warn_on, // +}; + +use core::{ + marker::PhantomData, + mem::ManuallyDrop, + ops::Deref, + ptr, + ptr::{ + drop_in_place, + NonNull, // + }, // +}; + +use kernel::{ + str::CString, + sync::{ + aref::{ + ARef, + AlwaysRefCounted, // + }, + atomic::{ + Atomic, + Relaxed, // + }, + rcu::rcu_barrier, // + }, + xarray::{ + AllocKind, + Reservation, + XArray, // + }, // +}; + +/// VTable for dma_fence backend_ops callbacks. +// +// Mandatory dma_fence backend_ops are implemented implicitly through +// [`FenceContext`]. Additional ones shall get implemented on this trait, = which +// then shall be demanded for the fence context data. +pub trait FenceContextOps { + /// The generic payload data for [`DriverFence`]s created on this fctx. + type FenceDataType: Send + Sync; +} + +/// A dma-fence context. A fence context takes care of associating related= fences +/// with each other, providing each with raising sequence numbers and a co= mmon +/// identifier. +#[pin_data(PinnedDrop)] +pub struct FenceContext { + /// The fence context number. + nr: u64, + /// The sequence number for the next fence created. + seqno: Atomic, + // The name parameters can be accessed by the dma_fence backend_ops. U= AF + // errors are prevented by the `call_rcu()` in `drop_driver_fence_data= ()`. + /// The name of the driver this FenceContext's fences belong to. + driver_name: CString, + /// The name of the timeline this FenceContext's fences belong to. + timeline_name: CString, + /// A list of all unsignaled fences of this context. + // As an additional safety-measure, the fence context will signal all = fences + // that were not yet signaled when it drops. + // + // The life-time on `DriverFence`s should typically prevent this from + // happening. + // + // However, we cannot fully guarantee in Rust that `DriverFence`s will= not + // be forgotten, e.g., through refcounting. This could circumvent the + // lifetime which intends to enforce that all fences disappear before = their + // context. + // + // Safety can still be ensured, though, by keeping track of unsignaled= fences + // and signalling those manually when the context drops. + #[pin] + fences: XArray>, + /// The user's data. + #[pin] + data: T, +} + +impl<'a, T: Send + Sync + FenceContextOps> FenceContext { + // This can later be extended as a vtable in case other parties need s= upport + // for the more "exotic" callbacks. + const OPS: bindings::dma_fence_ops =3D bindings::dma_fence_ops { + get_driver_name: Some(Self::get_driver_name), + get_timeline_name: Some(Self::get_timeline_name), + enable_signaling: None, + signaled: None, + wait: None, + release: None, + set_deadline: None, + }; + + /// Create a new `FenceContext`. + pub fn new( + initial_seqno: u64, + driver_name: CString, + timeline_name: CString, + data: impl PinInit, + ) -> impl PinInit + where + Error: From, + { + try_pin_init!(Self { + // SAFETY: `dma_fence_context_alloc()` merely works on a global + // atomic. Parameter `1` is the number of contexts we want to + // allocate. + nr: unsafe { bindings::dma_fence_context_alloc(1) }, + seqno: Atomic::new(initial_seqno), + driver_name, + timeline_name, + fences <- XArray::new(AllocKind::Alloc), + data <- data, + }) + } + + fn get_current_seqno(&self) -> u64 { + self.seqno.load(Relaxed) + } + + fn get_next_seqno(&self) -> u64 { + self.seqno.fetch_add(1, Relaxed) + } + + fn decrement_seqno(&self) { + let _ =3D self.seqno.fetch_sub(1, Relaxed); + } + + /// Allocate the memory for a [`DriverFence`] and already store `data`= inside. + /// + /// This is needed because many times, creation of a [`DriverFence`] m= ust not + /// fail, and allocating might deadlock in some situations. + /// + /// This reserves a sequence number for the future fence. In case of f= ailure, + /// the sequence number is decremented again. Since this is done with = atomic + /// operations, you might want to consider calling this function under= a + /// driver lock. + /// + /// The `data` you pass here must not perform any operations that are = illegal + /// in atomic context in its [`Drop`] implementation. + pub fn fence_alloc(&self, data: T::FenceDataType) -> Result> { + let fence_data =3D DriverFenceData { + rcu_head: Default::default(), + // `inner` remains uninitialized until a [`DriverFence`] takes= over. + inner: Fence { + inner: Opaque::uninit(), + }, + fctx: self, + data, + }; + + // In order to support the C dma_fence callbacks, it is necessary = for + // a `Fence` and a `DriverFence` to live in the same allocation, + // because the C backend passes a dma_fence, from which the driver= most + // likely wants to be able to access its `data` in `DriverFence`. + // + // Hence, we need the manage the memory manually. It will be freed= by the + // C backend automatically once the refcount within `Fence` drops = to 0. + let data =3D KBox::new(fence_data, GFP_KERNEL | __GFP_ZERO); + match data { + Ok(data) =3D> { + // Take the next seqno right now, so that we can use it fo= r pre-allocating + // an entry in the xarray. + let seqno: u64 =3D self.get_next_seqno(); + + match self.fences.reserve(seqno as usize, GFP_KERNEL) { + Ok(reservation) =3D> Ok(DriverFenceAllocation { + data, + seqno, + reservation, + ops: &Self::OPS, + }), + Err(e) =3D> { + self.decrement_seqno(); + Err(e) + } + } + } + Err(e) =3D> Err(e.into()), + } + } + + extern "C" fn get_driver_name(ptr: *mut bindings::dma_fence) -> *const= c_char { + // SAFETY: The C backend only invokes this callback with `ptr` poi= nting + // to a valid, unsignaled `bindings::dma_fence`. All fences create= d in + // this module always reside within `Fence` which always resides i= n a + // `DriverFenceData`, thus satisfying the function's safety + // requirements. + let fctx =3D unsafe { Self::from_raw_fence(ptr) }; + + fctx.driver_name.as_char_ptr() + } + + extern "C" fn get_timeline_name(ptr: *mut bindings::dma_fence) -> *con= st c_char { + // SAFETY: The C backend only invokes this callback with `ptr` poi= nting + // to a valid, unsignaled `bindings::dma_fence`. All fences create= d in + // this module always reside within `Fence` which always resides i= n a + // `DriverFenceData`, thus satisfying the function's safety + // requirements. + let fctx =3D unsafe { Self::from_raw_fence(ptr) }; + + fctx.timeline_name.as_char_ptr() + } + + /// Create a [`FenceContext`] from an associated [`bindings::dma_fence= `]. + /// + /// # Safety + /// + /// `ptr` must be a valid pointer to a [`bindings::dma_fence`] which r= esides + /// within a [`Fence`], which in turn resides in a [`DriverFenceData`]. + unsafe fn from_raw_fence(ptr: *mut bindings::dma_fence) -> &'a Self { + let opaque_fence =3D Opaque::cast_from(ptr); + + // SAFETY: Safe due to the function's overall safety requirements. + let fence_ptr =3D unsafe { container_of!(opaque_fence, Fence, inne= r) }; + + // CAST: `DriverFenceData` is repr(C) and a `Fence` is its first m= ember. + let fence_data_ptr =3D fence_ptr as *mut DriverFenceData<'a, T>; + + // SAFETY: Safe because of the comments directly above. + let fence_data =3D unsafe { &*fence_data_ptr }; + + fence_data.fctx + } +} + +// FenceContext's drop() ensures that the driver cannot unload while there= are +// still dma_fence callbacks running. This also prevents UAF problems with +// `fctx.driver_name` and `fctx.timeline_name`. +// +// DriverFence data gets dropped through call_rcu() in DriverFence::drop. +// This `rcu_barrier()` also serves to wait for their completion. +#[pinned_drop] +impl PinnedDrop for FenceContext { + fn drop(self: Pin<&mut Self>) { + let highest_seqno =3D self.get_current_seqno(); + + let fences_guard =3D self.fences.lock(); + + // TODO: Implement a public iterator for `XArray` and use it here. + for seqno in 0..highest_seqno { + if let Some(fence) =3D fences_guard.get(seqno as usize) { + let fence =3D fence.lock(); + + // SAFETY: We still hold a reference, thus the pointer is = valid. + let signaled: bool =3D + unsafe { bindings::dma_fence_test_signaled_flag(*fence= .deref()) }; + if !signaled { + // SAFETY: We still hold a reference, thus the pointer= is + // valid. The fence must not have been signaled yet, w= hich we + // check directly above. The lock must be held, which = we + // acquired above. + unsafe { bindings::dma_fence_set_error(*fence.deref(),= ECANCELED.to_errno()) }; + // SAFETY: We still hold a reference, thus the pointer= is + // valid. The lock must be held, which we acquired abo= ve. + unsafe { bindings::dma_fence_signal_locked(*fence.dere= f()) }; + } else { + pr_warn!("Signaled Fences in FenceContext."); + } + } + } + + drop(fences_guard); + rcu_barrier(); + } +} + +/// Error type for fence callback registration. +/// +/// Generic over `T` so that `AlreadySignaled` can return the callback to = the +/// caller, allowing it to reclaim any resources owned by the callback (e.= g., +/// a fence handle that needs to be signaled). +#[derive(Debug)] +pub enum CallbackError { + /// The fence was already signaled. The callback is returned so the ca= ller + /// can extract owned resources without losing them. + AlreadySignaled(T), + /// Some other error occurred during registration. + Other(Error), +} + +impl From> for Error { + fn from(err: CallbackError) -> Self { + match err { + CallbackError::AlreadySignaled(_) =3D> ENOENT, + CallbackError::Other(e) =3D> e, + } + } +} + +impl From for CallbackError { + fn from(e: AllocError) -> Self { + CallbackError::Other(Error::from(e)) + } +} + +/// Trait for callbacks that can be registered on fences. +/// +/// When the fence signals, the callback will be invoked. +/// +/// # Example +/// +/// ```rust +/// use kernel::dma_buf::FenceCallback; +/// +/// struct MyCallback { +/// // Your callback state here +/// } +/// +/// impl FenceCallback for MyCallback { +/// fn called(&mut self) { +/// pr_info!("Fence signaled!"); +/// // Handle fence completion +/// } +/// } +/// ``` +pub trait FenceCallback: Send + 'static { + /// Called when the fence is signaled. + /// + /// This is called from the fence signaling path, which may be in inte= rrupt + /// context or with locks held, which is why `self` is only borrowed, = so that + /// it cannot drop. Implementations must not sleep or perform + /// long-running operations. + /// + /// An implementation likely wants to inform itself (e.g., through a w= ork item) + /// within this callback that the associated [`FenceCallbackRegistrati= on`] + /// can now be dropped. + fn called(&mut self); +} + +/// A callback registration on a fence. +/// +/// When this object is dropped, the callback is automatically removed if = it +/// hasn't been called yet. +#[pin_data(PinnedDrop)] +pub struct FenceCallbackRegistration { + #[pin] + callback_foreign: Opaque, + callback: ManuallyDrop, + fence: ARef, +} + +impl FenceCallbackRegistration { + /// Create a [`PinInit`] closure for registering a callback on a fence. + /// + /// The actual attempt at registering the callback will take place onc= e you + /// call an allocator's `pin_init()` function. + /// + /// On success the callback is pinned in place and will fire when the = fence + /// signals. On `AlreadySignaled` the callback is returned to the call= er so + /// that owned resources can be reclaimed. + pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit> + 'a + where + T: 'a, + { + try_pin_init!(Self { + // We need to fully initialize the fence because after + // `dma_fence_add_callback()` ran, the callback might immediat= ely + // get invoked. + callback: ManuallyDrop::new(callback), + fence: ARef::from(fence), + callback_foreign <- Opaque::try_ffi_init(|ptr| { + // SAFETY: `fence.inner.get()` is a valid, initialized `st= ruct + // dma_fence`. `ptr` points to the `struct dma_fence_cb` f= ield + // within the pinned allocation, so it remains valid until + // `dma_fence_remove_callback()` in `PinnedDrop` or until = the + // callback fires. + let ret =3D unsafe { + to_result(bindings::dma_fence_add_callback( + fence.inner.get(), + ptr, + Some(Self::dma_fence_callback), + )) + }; + match ret { + Ok(()) =3D> Ok(()), + Err(e) =3D> { + if e =3D=3D ENOENT { + // SAFETY: We could not register the callback.= Thus, + // C will not use it. So we can just take it b= ack + // and pass it to the user again. + let cb_back =3D unsafe { ManuallyDrop::take(ca= llback) }; + Err(CallbackError::AlreadySignaled(cb_back)) + } else { + Err(CallbackError::Other(e)) + } + }, + } + }), + }? CallbackError) + } + + /// Raw dma fence callback that is called by the C code. + /// + /// # Safety + /// + /// This is only called by the dma_fence subsystem with valid pointers. + unsafe extern "C" fn dma_fence_callback( + _fence: *mut bindings::dma_fence, + callback_foreign: *mut bindings::dma_fence_cb, + ) { + let ptr =3D Opaque::cast_from(callback_foreign).cast_mut(); + + // SAFETY: All `cb` we can receive here have been created in such = a way + // that they are embedded into a `FenceCallbackRegistration`. The + // backend ensures synchronisation so whoever holds the registrati= on + // object cannot drop it while this code is running. See + // `FenceCallbackRegistration::drop`. + unsafe { + let reg: *mut Self =3D container_of!(ptr, Self, callback_forei= gn); + + (*reg).callback.called(); + } + } + + /// Returns a reference to the fence this callback is registered on. + pub fn fence(self: Pin<&Self>) -> &Fence { + &self.get_ref().fence + } +} + +#[pinned_drop] +impl PinnedDrop for FenceCallbackRegistration { + fn drop(self: Pin<&mut Self>) { + // Always call dma_fence_remove_callback, even if `callback` has a= lready + // been taken by `dma_fence_callback`. This is necessary for + // synchronization: `dma_fence_remove_callback` acquires `fence->l= ock`, + // which ensures that any in-flight `dma_fence_signal` (which call= s our + // callback while holding the same lock) has completed before we f= ree + // the struct. + // + // Without this, Drop can race with a concurrent signal: + // CPU0 (signal, lock held): take() -> signaled(fence_ref) (in p= rogress) + // CPU1 (drop): sees is_some()=3D=3Dfalse -> skips lock -> frees= struct + // CPU0: accesses fence_ref -> use-after-free + // + // When the callback has already fired, the signal path detached t= he + // list node via INIT_LIST_HEAD, so dma_fence_remove_callback just= sees + // an empty node and returns false =E2=80=94 the lock acquisition = is the only + // thing that matters. + // + // SAFETY: The fence pointer is valid and the cb was initialized by + // dma_fence_add_callback during construction. + unsafe { + bindings::dma_fence_remove_callback(self.fence.as_raw(), self.= callback_foreign.get()); + } + + // SAFETY: This is literally the drop implementation, so no one has + // dropped this so far; so we can do it now. + unsafe { ManuallyDrop::::drop(self.project().callback) }; + } +} + +// SAFETY: FenceCallbackRegistration can be sent between threads. +unsafe impl Send for FenceCallbackRegistration {} + +// SAFETY: &FenceCallbackRegistration can be shared between threads if &T = can. +unsafe impl Sync for FenceCallbackRegistration where = T: Sync {} + +/// The receiving counterpart of a [`DriverFence`]. +/// +/// The Rust DMA fence implementation has a dualistic design: [`DriverFenc= e`]s +/// are the producer-side, intended to be always owned by only one party. = That +/// party has the monopoly on signalling the fence. +/// +/// A [`Fence`] is the counterpart for consumers. Thus, [`Fence`]s are alw= ays +/// refcounted and can shared with an arbitrary number of parties, includi= ng +/// userspace. Hereby, a [`Fence`] can only be used for actions such as ch= ecking +/// the fence's status or for registering callbacks on it. +/// +/// Once the associated [`DriverFence`] signals, all +/// [`FenceCallbackRegistration`]s registered on the [`Fence`] will be exe= cuted. +/// +/// A [`Fence`] can arbitrarily outlive its [`DriverFence`] and the +/// [`FenceContext`]. Signalling a [`DriverFence`] decouples it from its +/// [`Fence`]s. +#[repr(transparent)] +pub struct Fence { + /// The actual dma_fence passed to C. + inner: Opaque, +} + +/// Guard helper for locking within this module. +struct FenceGuard { + inner: *mut bindings::dma_fence, + flags: usize, +} + +impl Deref for FenceGuard { + type Target =3D *mut bindings::dma_fence; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Drop for FenceGuard { + fn drop(&mut self) { + // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is + // merely a pointer to an integer, which lives as long as this fun= ction. + // When a `FenceGuard` exists, the lock has been taken by definiti= on. + unsafe { bindings::dma_fence_unlock_irqrestore(self.inner, &raw mu= t self.flags) }; + } +} + +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl Send for Fence {} +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl Sync for Fence {} + +impl Fence { + /// Check whether the fence was signaled at the moment of the function= call. + /// + /// Note that this can return `true` for a [`Fence`] whose [`DriverFen= ce`] + /// has not yet been dropped. The reason is that the fence ops callbac= ks can + /// cause the fence to get signaled by the C backend. + pub fn is_signaled(&self) -> bool { + // We should not use `dma_fence_is_signaled_locked()` here, because + // according to the C backend's recommendations, that function is + // problematic and we should avoid calling that function with a lo= ck + // held. + + // SAFETY: `fence` stems from `self`, which is valid by definition. + let ret =3D unsafe { bindings::dma_fence_is_signaled(self.as_raw()= ) }; + + // To be as robust as possible for the future we guarantee that an= API + // caller can 100% rely on the signalling being completed (i.e., a= ll + // fence callbacks ran), so we have to take the lock. + // + // The reason is that the C dma_fence backend currently does not + // carefully synchronize the `dma_fence_is_signaled()` function wi= th the + // proper spinlock. This can lead to the function returning `true`= while + // fence callbacks are still being executed. This can be mitigated= by + // guarding the entire function with the spinlock. + // + // The fundamental reason is that the C backend currently does gua= rd + // setting of the fence's signaled-bit with the fence's spinlock, = but + // reading is done locklessly. + // + // See commit c8a5d5ea3ba6a. + + let _ =3D self.lock(); + + ret + } + + fn lock(&self) -> FenceGuard { + let mut guard =3D FenceGuard { + inner: self.as_raw(), + flags: 0, + }; + + // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is + // merely a pointer to an integer, which lives as long as this fun= ction. + unsafe { bindings::dma_fence_lock_irqsave(guard.inner, &raw mut gu= ard.flags) }; + + guard + } + + /// Get the fence's sequence number. + pub fn get_seqno(&self) -> u64 { + // SAFETY: Valid because `self` is valid. + unsafe { (*self.as_raw()).seqno } + } + + fn as_raw(&self) -> *mut bindings::dma_fence { + self.inner.get() + } + + /// Create a [`Fence`] from a raw C [`bindings::dma_fence`]. + /// + /// # Safety + /// + /// `ptr` must point to an initialized fence that is embedded into a [= `Fence`]. + pub unsafe fn from_raw<'a>(ptr: *mut bindings::dma_fence) -> &'a Self { + // SAFETY: Safe as per the function's overall safety requirements. + unsafe { &*ptr.cast() } + } +} + +// SAFETY: These implement the C backends refcounting methods which are pr= oven +// to work correctly. +unsafe impl AlwaysRefCounted for Fence { + fn inc_ref(&self) { + // SAFETY: `self.as_raw()` is a pointer to a valid `struct dma_fen= ce`. + unsafe { bindings::dma_fence_get(self.as_raw()) } + } + + /// # Safety + /// + /// `ptr`must be a valid pointer to a [`DriverFence`]. + unsafe fn dec_ref(ptr: NonNull) { + // SAFETY: `ptr` is never a NULL pointer; and when `dec_ref()` is = called + // the fence is by definition still valid. + let fence =3D unsafe { (*ptr.as_ptr()).inner.get() }; + + // SAFETY: `fence` was created validly above. When `dec_ref()` is = called, + // there is by definition still a reference alive that can be put. + unsafe { bindings::dma_fence_put(fence) } + } +} + +// Necessary to guarantee that `inner` always comes first and can be freed= by C. +// Also useful for using casts instead of container_of(). +#[repr(C)] +#[pin_data] +struct DriverFenceData<'a, T: Send + Sync + FenceContextOps> { + #[pin] + /// The inner fence. + // Must always be the first member so that unsafe casting works; but a= lso + // necessary so that the C backend can free the allocation (coming fro= m our + // Rust code) with kfree_rcu(). + inner: Fence, + /// Callback head for dropping this in a deferred manner through RCU. + rcu_head: bindings::callback_head, + /// Reference to access the FenceContext. Useful for obtaining name pa= rameters. + fctx: &'a FenceContext, + /// The API user's data. This must either not need drop, or must delay= its + /// drop by a grace period. It is essential that the data only performs + /// operations legal in atomic context in its [`Drop`] implementation. + #[pin] + data: T::FenceDataType, +} + +/// A synchronization primitive mainly for GPU drivers. +/// +/// The Rust DMA fence implementation has a dualistic design: [`DriverFenc= e`]s +/// are the producer-side, intended to be always owned by only one party. = That +/// party has the monopoly on signalling the fence. +/// +/// A [`Fence`] is the counterpart for consumers. Thus, [`Fence`]s are alw= ays +/// refcounted and can shared with an arbitrary number of parties, includi= ng +/// userspace. Hereby, a [`Fence`] can only be used for actions such as ch= ecking +/// the fence's status or for registering callbacks on it. +/// +/// Once the associated [`DriverFence`] signals, all +/// [`FenceCallbackRegistration`]s registered on a [`Fence`] will be execu= ted. +/// +/// A [`Fence`] can arbitrarily outlive its [`DriverFence`] and the +/// [`FenceContext`]. Signalling a [`DriverFence`] decouples it from its +/// [`Fence`]s. +/// +/// It is crucial that a [`DriverFence`] always correctly represents the s= tate +/// of the associated job on the hardware. Especially, it is strictly nece= ssary +/// that the owner ensures that all [`DriverFence`]s get eventually signal= ed. +/// As a last ressort, a [`DriverFence`] will signal itself if it drops +/// unsignaled. +/// +/// This design intends to implement the [`bindings::dma_fence_ops`] in su= ch a +/// way that the driver-data necessary to implement the callback's functio= nality +/// resides in the [`FenceContext`]. Thus, a [`DriverFence`] contains a +/// reference to the context, which can be accessed in the callbacks. The +/// implementation, therefore, ensures that a [`DriverFence`] cannot outli= ve its +/// [`FenceContext`]. Unfortunately, this can be circumvented under certain +/// circumstances in Rust. To mitigate this, the [`FenceContext`] will sig= nal +/// all unsignaled [`DriverFence`]s in the unlikely case of it dropping be= fore +/// the last fence. +/// +/// # Examples +/// +/// ``` +/// use kernel::dma_buf::{ +/// DriverFence, +/// FenceContext, +/// FenceContextOps, +/// FenceCallback, +/// FenceCallbackRegistration, // +/// }; +/// use kernel::str::CString; +/// use kernel::sync::aref::ARef; +/// use core::fmt::Display; +/// +/// struct CallbackData { } +/// +/// impl FenceCallback for CallbackData { +/// fn called(&mut self) { +/// pr_info!("DmaFence callback executed.\n"); +/// } +/// } +/// +/// #[pin_data] +/// struct FenceContextData {} +/// +/// impl FenceContextData { +/// fn new() -> impl PinInit { +/// pin_init!(Self {}) +/// } +/// } +/// +/// impl FenceContextOps for FenceContextData { +/// type FenceDataType =3D FenceData; +/// } +/// +/// let fctx_data =3D FenceContextData::new(); +/// +/// let driver_name =3D CString::try_from_fmt(fmt!("dummy_driver"))?; +/// let timeline_name =3D CString::try_from_fmt(fmt!("dummy_timeline"))?; +/// +/// let fctx =3D KBox::pin_init(FenceContext::new(0, driver_name, timeline= _name, fctx_data), GFP_KERNEL)?; +/// +/// struct FenceData { +/// data: CString, +/// } +/// +/// let data =3D CString::try_from_fmt(fmt!("dummy_data"))?; +/// let fence_data =3D FenceData { data }; +/// +/// let fence_alloc =3D fctx.fence_alloc(fence_data)?; +/// let mut fence =3D fence_alloc.new_fence(); +/// +/// let cb_data =3D CallbackData { }; +/// let waiting_fence =3D ARef::from(fence.as_fence()); +/// let cb_reg =3D FenceCallbackRegistration::new(&waiting_fence, cb_data); +/// let cb_reg =3D KBox::pin_init(cb_reg, GFP_KERNEL)?; +/// +/// // TODO signalling guards +/// fence.signal(Ok(())); +/// assert_eq!(waiting_fence.is_signaled(), true); +/// +/// Ok::<(), Error>(()) +/// ``` +pub struct DriverFence<'a, T: Send + Sync + FenceContextOps> { + /// The actual content of the fence. Lives in a [`NonNull`] so that its + /// memory can be managed independently. Valid until both the [`Driver= Fence`] + /// and all associated [`Fence`]s have disappeared. + data: NonNull>, +} + +/// A pre-prepared DMA fence, carrying the user's data and the memory it a= nd the +/// fence reside in. Only useful for creating a [`DriverFence`]. Splitting +/// allocation and full initialization is necessary because fences cannot = be +/// allocated dynamically in some circumstances (deadlock). +pub struct DriverFenceAllocation<'a, T: Send + Sync + FenceContextOps> { + /// The memory for the actual content of the fence. + /// Handed over to a [`DriverFence`], or deallocated once the + /// [`DriverFenceAllocation`] drops. + data: KBox>, + /// Pointer for the ops for the associated [`FenceContext`] + ops: *const bindings::dma_fence_ops, + /// The fence's future sequence number. Needed early on for preallocat= ing an + /// entry in the xarray in [`FenceContext`]. + seqno: u64, + /// The reservation for `seqno` in the [`FenceContext`]'s xarray. + reservation: Reservation<'a, ARef>, +} + +impl<'a, T: Send + Sync + FenceContextOps> DriverFenceAllocation<'a, T> { + /// Create a new fence, consuming `data`. + /// + /// The fence will increment the refcount of the fence context associa= ted with this + /// [`FenceContext`]. + pub fn new_fence(self) -> DriverFence<'a, T> { + // We feed the C dma_fence backend a NULL for the spinlock so that= it + // uses per-fence locks automatically. + let null_ptr: *mut bindings::spinlock =3D ptr::null_mut(); + let fence_ptr =3D self.as_raw(); + // SAFETY: `fence_ptr` has been created directly above. It will li= ve + // at least as long as `Self`. The same applies to `&Self::OPS`. + unsafe { + bindings::dma_fence_init(fence_ptr, self.ops, null_ptr, self.d= ata.fctx.nr, self.seqno) + }; + + let fence =3D ARef::from(&self.data.inner); + let mut fences_guard =3D self.data.fctx.fences.lock(); + // Can never be an error because we already allocated an entry for + // exactly this sequence number. + // TODO: write some sort of `XArray::replace()` function or mechan= ism for + // reserving entries so that this can obviously never fail. + if fences_guard + .store(self.seqno as usize, fence, GFP_KERNEL) + .is_err() + { + pr_warn_once!("Storing to fctx xarray failed.\n"); + } + + // Dropping the reservation takes the xarray lock, so first releas= e it + // by hand right here to avoid deadlock. + drop(fences_guard); + // We have stored to the reserved entry and can drop the reservati= on. + drop(self.reservation); + + // A `DriverFenceAllocation`'s purpose is to carry allocated memor= y, so that + // `DriverFence`s can always be created without allocating. In this + // method, ownership over that memory is transferred to the new + // `DriverFence` and managed through refcounting. The C dma_fence + // backend will ultimately free the memory once the refcount reach= es 0. + let ptr =3D KBox::into_raw(self.data); + // SAFETY: `ptr` was just created validly directly above. + let ptr =3D unsafe { NonNull::new_unchecked(ptr) }; + + DriverFence { data: ptr } + } + + fn as_raw(&self) -> *mut bindings::dma_fence { + self.data.inner.inner.get() + } +} + +impl<'a, T: Send + Sync + FenceContextOps> DriverFence<'a, T> { + fn as_raw(&self) -> *mut bindings::dma_fence { + // SAFETY: Valid because `self` is valid. + let fence_data =3D unsafe { &*self.data.as_ptr() }; + + fence_data.inner.inner.get() + } + + /// Create a [`DriverFence`] from a raw pointer to a [`bindings::dma_f= ence`]. + /// + /// # Safety + /// + /// `ptr` must be a valid pointer to a `dma_fence` that was obtained t= hrough + /// a [`DriverFence`] with matching generic data for both fence and as= sociated + /// [`FenceContext`]. + unsafe fn from_raw(ptr: *mut bindings::dma_fence) -> Self { + let opaque_fence =3D Opaque::cast_from(ptr); + + // SAFETY: Safe due to the function's overall safety requirements. + let fence_ptr =3D unsafe { container_of!(opaque_fence, Fence, inne= r) }; + + // DriverFenceData is repr(C) and a Fence is its first member. + let fence_data_ptr =3D fence_ptr as *mut DriverFenceData<'a, T>; + + // SAFETY: `fence_data_ptr` was created validly above. + let data =3D unsafe { NonNull::new_unchecked(fence_data_ptr) }; + + Self { data } + } + + /// Return the underlying [`Fence`]. + pub fn as_fence(&self) -> &Fence { + // SAFETY: `self` is by definition still valid, and it cannot drop= until + // this new reference is gone. + unsafe { Fence::from_raw(self.as_raw()) } + } + + /// Signal the fence. This will invoke all registered callbacks. + pub fn signal(self, res: Result) { + // We signal this fence now. Remove it from the list of unsignaled= fences. + + // SAFETY: `self` is valid, and since this `DriverFence` was initi= alized + // `self.data` is valid, too. + // + // TODO: move this, could be a super unlikely race + let fence_data =3D unsafe { self.data.as_ref() }; + let mut fences_guard =3D fence_data.fctx.fences.lock(); + let _ =3D fences_guard.remove(self.as_fence().get_seqno() as usize= ); + drop(fences_guard); + + let fence =3D self.as_fence().lock(); + + // SAFETY: `fence` is valid because `self` is valid. The lock must= be + // held, which we acquired directly above. + if !unsafe { bindings::dma_fence_test_signaled_flag(*fence.deref()= ) } { + if let Err(err) =3D res { + // SAFETY: `fence` is valid because `self` is valid. The f= ence + // must not have been signaled yet, which we check directl= y above. + unsafe { bindings::dma_fence_set_error(*fence.deref(), err= .to_errno()) }; + } + // SAFETY: `fence` is valid because `self` is valid. The lock = must + // be held, which we acquired above. + unsafe { bindings::dma_fence_signal_locked(*fence.deref()) }; + } + } +} + +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl<'a, T: Send + Sync + FenceContextOps> Send for DriverFence<'a,= T> {} +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl<'a, T: Send + Sync + FenceContextOps> Sync for DriverFence<'a,= T> {} + +impl<'a, T: Send + Sync + FenceContextOps> Deref for DriverFence<'a, T> { + type Target =3D T::FenceDataType; + + fn deref(&self) -> &Self::Target { + // SAFETY: Thanks to refcounting, `data` is always valid as long a= s `self` is. + let data =3D unsafe { &*self.data.as_ptr() }; + + &data.data + } +} + +/// A borrow wrapper for [`DriverFence`]. Implements [`Deref`]. +pub struct DriverFenceBorrow<'a, T: Send + Sync + FenceContextOps> { + driver_fence: ManuallyDrop>, + _lifetime: PhantomData<&'a T>, +} + +impl<'a, T: Send + Sync + FenceContextOps> Deref for DriverFenceBorrow<'a,= T> { + type Target =3D DriverFence<'a, T>; + + fn deref(&self) -> &Self::Target { + self.driver_fence.deref() + } +} + +// SAFETY: The Rust dma_fence abstractions are already designed around the= inner +// C `dma_fence`, which can serve safely as the identification point when = being +// owned by C. Moreover, safety is ensured by not dropping `DriverFence` a= nd by +// only allowing operations without side effects on the Borrowed type. +unsafe impl ForeignOwnable for= DriverFence<'_, T> { + type Borrowed<'a> + =3D DriverFenceBorrow<'a, T> + where + Self: 'a; + type BorrowedMut<'a> + =3D DriverFenceBorrow<'a, T> + where + Self: 'a; + + const FOREIGN_ALIGN: usize =3D core::mem::align_of::(); + + fn into_foreign(self) -> *mut c_void { + let fence =3D self; + + let ptr =3D fence.as_raw(); + + // DriverFence must not drop. + let _ =3D ManuallyDrop::new(fence); + + ptr.cast() + } + + unsafe fn from_foreign(ptr: *mut c_void) -> Self { + // SAFETY: Safe because the trait implementation only invokes this= with + // a valid `ptr`, associated to a `DriverFence` with matching gene= ric data. + unsafe { Self::from_raw(ptr.cast()) } + } + + unsafe fn borrow<'a>(ptr: *mut c_void) -> Self::Borrowed<'a> + where + Self: 'a, + { + // SAFETY: The trait implementation ensures that `ptr` always resi= des + // within a [`Fence`] within a [`DriverFenceData`]. + let driver_fence =3D unsafe { Self::from_raw(ptr.cast()) }; + + let driver_fence =3D ManuallyDrop::new(driver_fence); + + DriverFenceBorrow { + driver_fence, + _lifetime: PhantomData, + } + } + + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> Self::BorrowedMut<'a> + // FIXME: The bound below and the one above in `borrow` should actuall= y be + // unnecessary since the compiler should be able to completely derive = all + // necessary information automatically. There is currently a compiler = bug + // preventing that, though: + // + // https://github.com/rust-lang/rust/issues/155430. + // + // (Help to) fix the compiler bug and remove the bounds afterwards. + where + Self: 'a, + { + // SAFETY: The trait implementation ensures that `ptr` always resi= des + // within a [`Fence`] within a [`DriverFenceData`]. + let driver_fence =3D unsafe { Self::from_raw(ptr.cast()) }; + + let driver_fence =3D ManuallyDrop::new(driver_fence); + + DriverFenceBorrow { + driver_fence, + _lifetime: PhantomData, + } + } +} + +impl<'a, T: Send + Sync + FenceContextOps> Drop for DriverFence<'a, T> { + fn drop(&mut self) { + let guard =3D self.as_fence().lock(); + + // Use dma_fence_test_signaled_flag() instead of + // dma_fence_is_signaled_locked() because the C backend wants to g= et rid + // of the latter. + + // SAFETY: `guard` is valid until the `call_rcu()` below. + let signaled: bool =3D unsafe { bindings::dma_fence_test_signaled_= flag(*guard.deref()) }; + if warn_on!(!signaled) { + // SAFETY: `guard` is valid until the `call_rcu()` below. The = fence + // must not have been signaled yet, which we check directly ab= ove. + unsafe { bindings::dma_fence_set_error(*guard.deref(), ECANCEL= ED.to_errno()) }; + // SAFETY: `guard` is valid until the `call_rcu()` below. The = lock + // must be held, which we acquired above. + unsafe { bindings::dma_fence_signal_locked(*guard.deref()) }; + } + drop(guard); + + // SAFETY: Valid because `self` is valid. + let rcu_head_ptr =3D unsafe { &raw mut (*self.data.as_ptr()).rcu_h= ead }; + + // `DriverFenceData` but could be accessed through some dma_fence + // callbacks right now. Access is being revoked in principle above= by + // signalling the fence, but since the C backend does not guarantee + // perfect full synchronization, we have to wait for one grace per= iod to + // ensure that all accessors of `DriverFenceData` (through the + // dma_fence_ops accessible through a `Fence`) are gone. + + // SAFETY: `call_rcu()` is always safe to be called. `rcu_head_ptr= ` was + // created validly above. The module must perform a `synchronize_r= cu()` + // or `rcu_barrier()` call to guard against module unload. + unsafe { bindings::call_rcu(rcu_head_ptr, Some(drop_driver_fence_d= ata::)) }; + } +} + +// TODO: +// The entire call_rcu() mechanism in the drop above and the code below wo= uld be +// unnecessary if C's dma_fence_signal() could be reworked in a way that a= fter it +// ran, the caller knows that no fence_ops callbacks can be running anymor= e. +// In other words, if the dma_fence backend would use its spinlock for full +// synchronization. +// +// Then we could move the drop_in_place() and dma_fence_put() upwards into= the +// drop() implementation and call it a day. + +/// Finally really drop this `DriverFence` +/// +/// # Safety +/// +/// `head` references the `rcu_head` field of an `DriverFenceData`. All +/// accessors to that `DriverFenceData` must be gone by now. This must = be +/// ensured by signalling the associated `DriverFence` and then waiting +/// for a grace period until calling this function here. +unsafe extern "C" fn drop_driver_fence_data( + head: *mut bindings::callback_head, +) { + // SAFETY: Caller provides a pointer to the `rcu_head` field of a `Dri= verFenceData`. + let fence_data =3D unsafe { container_of!(head, DriverFenceData<'_, T>= , rcu_head) }; + + // SAFETY: `fence_data` was created validly above. All the fence's dat= a will + // only drop below, but the raw pointer to the raw C `dma_fence` remai= ns + // valid because the reference count is only decremented at the end of= the + // function. + let fence =3D unsafe { (*fence_data).inner.inner.get() }; + + // SAFETY: `fence_data` was created validly above. A grace period has = passed. + // All callbacks which might have had access to the `fctx` are gone no= w. + unsafe { drop_in_place(&raw mut (*fence_data).fctx) }; + + // SAFETY: `fence_data` was created validly above. The user has already + // dropped the only conventional accessor to the user data, the `Drive= rFence`, + // one grace period ago. All accessors are gone now. + unsafe { drop_in_place(&raw mut (*fence_data).data) }; + + // The inner `Fence` explicitly does not get dropped because there may= be + // many more users / consumers, each holding their own reference. + + // SAFETY: Once a `DriverFence` is initialized, the inner `fence` is + // valid and initialized. It is valid until the refcount drops + // to 0, which can earliest happen once we drop the `DriverFence`'s re= ference + // here. + unsafe { bindings::dma_fence_put(fence) }; + + // The actual memory the data associated with a `DriverFence` lives in + // gets freed by the C dma_fence backend once the fence's refcount rea= ches 0. +} diff --git a/rust/kernel/dma_buf/mod.rs b/rust/kernel/dma_buf/mod.rs new file mode 100644 index 000000000000..4764a828642e --- /dev/null +++ b/rust/kernel/dma_buf/mod.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! DMA-buf subsystem abstractions. + +pub mod dma_fence; + +pub use self::dma_fence::{ + DriverFence, + Fence, + FenceCallback, + FenceCallbackRegistration, + FenceContext, + FenceContextOps, // +}; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9512af7156df..dc54d8af471f 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -64,6 +64,7 @@ pub mod device_id; pub mod devres; pub mod dma; +pub mod dma_buf; pub mod driver; #[cfg(CONFIG_DRM =3D "y")] pub mod drm; --=20 2.55.0 From nobody Fri Jul 24 22:54:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 E8C4A37207D; Wed, 22 Jul 2026 14:51:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731871; cv=none; b=rQTD84vFLIkOqVg875JPlRNYZ5Si+x+xW2JYF5+ckwE8yMcOuyA3TgFO0QQPo5mF0t7SkOHgG/AdqE6/GJKFs4d7t6lmuGY/YpO3KoAu1vcbvd6Wgf9+YmjAhu+qH9YK6dGATj39EqBJ2JOPaRJtj3BXAzSaw2L4dCddA7VZOO8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784731871; c=relaxed/simple; bh=a2JzSHpl1u9rJ6J+B/5Ln6ZHvZE8FVqmZtBCNW+d51I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=O3TFzZTH1rvDMIQxcfAdYGFrxQFjaqxkafk9vZsYmbG+NuTsgQirjB3oC0roOUjzpLJTRy3l/mRdkpwewK9po2H5DrbyGg33ZzOzjoJeDcTtn3gPF+jnR6lSp0Vj43Z/hzDVj4uBCMS0j0VOXYdcYMjE3j4jNSTwSGjq/Tj8HKU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eFze4hgc; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="eFze4hgc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 156EA1F000E9; Wed, 22 Jul 2026 14:51:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784731869; bh=4+kdpOSbYNuwg9iO0Aocs3O30U1qLI1bO+WgZ1kcKkQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=eFze4hgcsvO59JcuBCrNf5eYU1oZfBzqbKWg26kkzcyJ8dgZpWsOkkaXLVRybnGXN 3j98h8baBTR530HbWrsyQF3irXTkwA2a81dVCmEDdTCbcdX9eCJSfLJ3Kk9VVCt3lk Ch4EdZB4CbZXbEYynMj0duN90jV7eeNbpnrbJs3ZVi4abwUIajJLHiigFLnTyix9dJ 8PiGHjlnfFuCyCOt/RV+bZoz0CjSRF7w3OIpnaV7AvU7dRbfhI6+NdYmP8r/l5Dxz9 lAj8FaiTdxjk5BFjVm97DIZ3CDkcA6hDi3hXPkFtRJ4JrVil5OowRArSqll3b1oEyW Pjry3JLOLQziQ== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?UTF-8?q?Onur=20=C3=96zkan?= , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , Lyude Paul , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Greg Kroah-Hartman , "Yury Norov (NVIDIA)" , Asahi Lina , Lorenzo Stoakes , FUJITA Tomonori , Eliot Courtney , Mirko Adzic , Daniel del Castillo , Alistair Francis Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH v6 6/6] MAINTAINERS: Add entry for Rust dma-buf Date: Wed, 22 Jul 2026 16:49:30 +0200 Message-ID: <20260722144931.1070312-8-phasta@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260722144931.1070312-2-phasta@kernel.org> References: <20260722144931.1070312-2-phasta@kernel.org> 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 Rust does now have abstractions for dma_fence. These abstractions are quite complicated and require expertise with both the C and the Rust side. Therefore, using the existing entry also for maintenance of the Rust code appears reasonable. Philipp volunteers to help maintain the dma_fence abstractions. Add a corresponding MAINTAINERS entry. Signed-off-by: Philipp Stanner Acked-by: Christian K=C3=B6nig Acked-by: Sumit Semwal --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..5e5a23a21cd8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7622,6 +7622,7 @@ F: fs/dlm/ DMA BUFFER SHARING FRAMEWORK M: Sumit Semwal M: Christian K=C3=B6nig +M: Philipp Stanner L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers) @@ -7635,6 +7636,8 @@ F: include/linux/dma-buf.h F: include/linux/dma-buf/ F: include/linux/dma-resv.h F: rust/helpers/dma-resv.c +F: rust/helpers/dma_fence.c +F: rust/kernel/dma_buf/ K: \bdma_(?:buf|fence|resv)\b =20 DMA GENERIC OFFLOAD ENGINE SUBSYSTEM @@ -8621,7 +8624,9 @@ T: git https://gitlab.freedesktop.org/drm/rust/kernel= .git F: drivers/gpu/drm/nova/ F: drivers/gpu/drm/tyr/ F: drivers/gpu/nova-core/ +F: rust/helpers/dma_fence.c F: rust/helpers/gpu.c +F: rust/kernel/dma_buf/ F: rust/kernel/drm/ F: rust/kernel/gpu.rs F: rust/kernel/gpu/ --=20 2.55.0