rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Danilo Krummrich <dakr@redhat.com>
To: gregkh@linuxfoundation.org, rafael@kernel.org,
	bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com,
	wedsonaf@gmail.com, boqun.feng@gmail.com, gary@garyguo.net,
	bjorn3_gh@protonmail.com, benno.lossin@proton.me,
	a.hindborg@samsung.com, aliceryhl@google.com, airlied@gmail.com,
	fujita.tomonori@gmail.com, lina@asahilina.net,
	pstanner@redhat.com, ajanulgu@redhat.com, lyude@redhat.com
Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-pci@vger.kernel.org, Danilo Krummrich <dakr@redhat.com>
Subject: [RFC PATCH 08/11] rust: add devres abstraction
Date: Mon, 20 May 2024 19:25:45 +0200	[thread overview]
Message-ID: <20240520172554.182094-9-dakr@redhat.com> (raw)
In-Reply-To: <20240520172554.182094-1-dakr@redhat.com>

Add a Rust abstraction for the kernel's devres (device resource
management) implementation.

The Devres type acts as a container to manage the lifetime and
accessibility of device bound resources. Therefore it registers a
devres callback and revokes access to the resource on invocation.

Users of the Devres abstraction can simply free the corresponding
resources in their Drop implementation, which is invoked when either the
Devres instance goes out of scope or the devres callback leads to the
resource being revoked, which implies a call to drop_in_place().

Co-developed-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@redhat.com>
---
 rust/helpers.c        |   5 ++
 rust/kernel/devres.rs | 151 ++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs    |   1 +
 3 files changed, 157 insertions(+)
 create mode 100644 rust/kernel/devres.rs

diff --git a/rust/helpers.c b/rust/helpers.c
index 1d3e800140fc..34061eca05a0 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -173,6 +173,11 @@ void rust_helper_rcu_read_unlock(void)
 EXPORT_SYMBOL_GPL(rust_helper_rcu_read_unlock);
 /* end rcu */
 
+int rust_helper_devm_add_action(struct device *dev, void (*action)(void *), void *data)
+{
+	return devm_add_action(dev, action, data);
+}
+
 /*
  * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
  * use it in contexts where Rust expects a `usize` like slice (array) indices.
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
new file mode 100644
index 000000000000..bf7bd304cd9b
--- /dev/null
+++ b/rust/kernel/devres.rs
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Devres abstraction
+//!
+//! [`Devres`] represents an abstraction for the kernel devres (device resource management)
+//! implementation.
+
+use crate::{
+    alloc::Flags,
+    bindings,
+    device::Device,
+    error::{Error, Result},
+    prelude::*,
+    revocable::Revocable,
+    types::ARef,
+};
+
+use core::ffi::c_void;
+use core::ops::Deref;
+
+#[pin_data]
+struct DevresInner<T> {
+    dev: ARef<Device>,
+    #[pin]
+    data: Revocable<T>,
+}
+
+/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
+/// manage their lifetime.
+///
+/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
+/// [`Device`] is unbound respectively, depending on what happens first.
+///
+/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
+/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
+///
+/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
+/// anymore.
+///
+/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
+/// [`Drop`] implementation.
+///
+/// # Example
+///
+/// ```
+/// use kernel::devres::Devres;
+///
+/// // See also [`pci::Bar`] for a real example.
+/// struct IoRemap(IoMem);
+///
+/// impl IoRemap {
+///     fn new(usize paddr, usize len) -> Result<Self>{
+///         // assert success
+///         let addr = unsafe { bindings::ioremap(paddr as _); };
+///         let iomem = IoMem::new(addr, len)?;
+///
+///         Ok(IoRemap(iomem))
+///     }
+/// }
+///
+/// impl Drop for IoRemap {
+///     fn drop(&mut self) {
+///         unsafe { bindings::iounmap(self.0.ioptr as _); };
+///     }
+/// }
+///
+/// impl Deref for IoRemap {
+///    type Target = IoMem;
+///
+///    fn deref(&self) -> &Self::Target {
+///        &self.0
+///    }
+/// }
+///
+/// let devres = Devres::new(dev, IoRemap::new(0xBAAAAAAD, 0x4)?, GFP_KERNEL)?;
+///
+/// let res = devres.try_access().ok_or(ENXIO)?;
+/// res.writel(0xBAD);
+/// ```
+///
+pub struct Devres<T> {
+    inner: Pin<Box<DevresInner<T>>>,
+    callback: unsafe extern "C" fn(*mut c_void),
+}
+
+impl<T> DevresInner<T> {
+    fn as_ptr(&self) -> *const DevresInner<T> {
+        self as *const DevresInner<T>
+    }
+
+    fn as_cptr(&self) -> *mut c_void {
+        self.as_ptr() as *mut c_void
+    }
+}
+
+unsafe extern "C" fn devres_callback<T>(inner: *mut c_void) {
+    let inner = inner as *const DevresInner<T>;
+    let inner = unsafe { &*inner };
+
+    inner.data.revoke();
+}
+
+impl<T> Devres<T> {
+    /// Creates a new [`Devres`] instance of the give data.
+    pub fn new(dev: ARef<Device>, data: T, flags: Flags) -> Result<Self> {
+        let callback = devres_callback::<T>;
+
+        let inner = Box::pin_init(
+            pin_init!( DevresInner {
+                dev: dev,
+                data <- Revocable::new(data),
+            }),
+            flags,
+        )?;
+
+        let ret = unsafe {
+            bindings::devm_add_action(inner.dev.as_raw(), Some(callback), inner.as_cptr())
+        };
+
+        if ret != 0 {
+            return Err(Error::from_errno(ret));
+        }
+
+        // We have to store the exact callback function pointer used with
+        // `bindings::devm_add_action` for `bindings::devm_remove_action`. There compiler might put
+        // multiple definitions of `devres_callback<T>` for the same `T` in both the kernel itself
+        // and modules. Hence, we might see different pointer values depending on whether we look
+        // at `devres_callback<T>`'s address from `Devres::new` or `Devres::drop`.
+        Ok(Devres { inner, callback })
+    }
+}
+
+impl<T> Deref for Devres<T> {
+    type Target = Revocable<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner.data
+    }
+}
+
+impl<T> Drop for Devres<T> {
+    fn drop(&mut self) {
+        unsafe {
+            bindings::devm_remove_action(
+                self.inner.dev.as_raw(),
+                Some(self.callback),
+                self.inner.as_cptr(),
+            )
+        }
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index d7d415429517..11645060b444 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -30,6 +30,7 @@
 pub mod alloc;
 mod build_assert;
 pub mod device;
+pub mod devres;
 pub mod driver;
 pub mod error;
 pub mod init;
-- 
2.45.1


  parent reply	other threads:[~2024-05-20 17:27 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-20 17:25 [RFC PATCH 00/11] [RFC] Device / Driver and PCI Rust abstractions Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 01/11] rust: add abstraction for struct device Danilo Krummrich
2024-05-20 18:00   ` Greg KH
2024-05-20 18:24     ` Miguel Ojeda
2024-05-20 20:22     ` Danilo Krummrich
2024-05-21  9:24       ` Greg KH
2024-05-21 20:42         ` Danilo Krummrich
2024-06-04 14:17           ` Greg KH
2024-06-04 16:23             ` Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 02/11] rust: add driver abstraction Danilo Krummrich
2024-05-20 18:14   ` Greg KH
2024-05-20 22:30     ` Danilo Krummrich
2024-05-21  9:35       ` Greg KH
2024-05-21  9:59         ` Greg KH
2024-05-21 22:21         ` Danilo Krummrich
2024-06-04 14:27           ` Greg KH
2024-06-04 15:41             ` Danilo Krummrich
2024-06-04 16:00               ` Greg KH
2024-06-04 20:06                 ` Danilo Krummrich
2024-05-21  5:42     ` Dave Airlie
2024-05-21  8:04       ` Greg KH
2024-05-21 22:42         ` Danilo Krummrich
2024-05-29 11:10   ` Dirk Behme
2024-05-30  5:58   ` Dirk Behme
2024-05-20 17:25 ` [RFC PATCH 03/11] rust: add rcu abstraction Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 04/11] rust: add revocable mutex Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 05/11] rust: add revocable objects Danilo Krummrich
2024-05-31  8:35   ` Dirk Behme
2024-05-20 17:25 ` [RFC PATCH 06/11] rust: add device::Data Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 07/11] rust: add `dev_*` print macros Danilo Krummrich
2024-05-20 17:25 ` Danilo Krummrich [this message]
2024-05-29 12:00   ` [RFC PATCH 08/11] rust: add devres abstraction Dirk Behme
2024-06-03  7:20   ` Dirk Behme
2024-05-20 17:25 ` [RFC PATCH 09/11] rust: add basic PCI driver abstractions Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 10/11] rust: add basic abstractions for iomem operations Danilo Krummrich
2024-05-20 22:32   ` Miguel Ojeda
2024-05-21  2:07     ` Dave Airlie
2024-05-21  3:01       ` Wedson Almeida Filho
2024-05-21  8:03         ` Philipp Stanner
2024-05-25 19:24           ` Wedson Almeida Filho
2024-05-21  2:59     ` Danilo Krummrich
2024-05-21  7:36     ` Philipp Stanner
2024-05-21  9:18       ` Miguel Ojeda
2024-05-21 18:36         ` Danilo Krummrich
2024-05-20 17:25 ` [RFC PATCH 11/11] rust: PCI: add BAR request and ioremap Danilo Krummrich
2024-05-20 23:27   ` Miguel Ojeda
2024-05-21 11:22     ` Danilo Krummrich
2024-05-20 18:14 ` [RFC PATCH 00/11] [RFC] Device / Driver and PCI Rust abstractions Greg KH
2024-05-20 18:16   ` Greg KH
2024-05-20 19:50     ` Danilo Krummrich
2024-05-21  9:21       ` Greg KH

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240520172554.182094-9-dakr@redhat.com \
    --to=dakr@redhat.com \
    --cc=a.hindborg@samsung.com \
    --cc=airlied@gmail.com \
    --cc=ajanulgu@redhat.com \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=benno.lossin@proton.me \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=fujita.tomonori@gmail.com \
    --cc=gary@garyguo.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=lina@asahilina.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=pstanner@redhat.com \
    --cc=rafael@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=wedsonaf@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).