rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/3] Implement TryFrom<&Device> for bus specific devices
@ 2025-03-21 14:57 Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 1/3] rust: device: implement bus_type_raw() Danilo Krummrich
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Danilo Krummrich @ 2025-03-21 14:57 UTC (permalink / raw)
  To: bhelgaas, gregkh, rafael, ojeda, alex.gaynor, boqun.feng, gary,
	bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, tmgross
  Cc: linux-pci, rust-for-linux, linux-kernel, Danilo Krummrich

This series provides a mechanism to safely convert a struct device into its
corresponding bus specific device instance, if any.

In C a generic struct device is typically converted to a specific bus device
with container_of(). This requires the caller to know whether the generic struct
device is indeed embedded within the expected bus specific device type.

In Rust we can do the same thing by implementing the TryFrom trait, e.g.

        impl TryFrom<&Device> for pci::Device

This is a safe operation, since we can check whether dev->bus equals the the
expected struct bus_type.

A branch containing the patches can be found in [1].

This is needed for the auxiliary bus abstractions and connecting nova-core with
nova-drm. [2]

[1] https://web.git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust/device
[2] https://gitlab.freedesktop.org/drm/nova/-/tree/staging/nova-drm

Changes in v3:
  - drop patch to add Device::parent(), will make it crate private and part of
    the auxbus series
  - safety comment: clarify that a device' bus type is guaranteed to be set
    correctly by the corresponding C code

Changes in v2:
  - s/unsafe { *self.as_raw() }.parent/unsafe { (*self.as_raw()).parent }/
  - expand safety comment on Device::bus_type_raw()

Danilo Krummrich (3):
  rust: device: implement bus_type_raw()
  rust: pci: impl TryFrom<&Device> for &pci::Device
  rust: platform: impl TryFrom<&Device> for &platform::Device

 rust/kernel/device.rs   |  9 +++++++++
 rust/kernel/pci.rs      | 22 ++++++++++++++++++++--
 rust/kernel/platform.rs | 22 ++++++++++++++++++++--
 3 files changed, 49 insertions(+), 4 deletions(-)


base-commit: 51d0de7596a458096756c895cfed6bc4a7ecac10
-- 
2.48.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 1/3] rust: device: implement bus_type_raw()
  2025-03-21 14:57 [PATCH v3 0/3] Implement TryFrom<&Device> for bus specific devices Danilo Krummrich
@ 2025-03-21 14:57 ` Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 2/3] rust: pci: impl TryFrom<&Device> for &pci::Device Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 3/3] rust: platform: impl TryFrom<&Device> for &platform::Device Danilo Krummrich
  2 siblings, 0 replies; 4+ messages in thread
From: Danilo Krummrich @ 2025-03-21 14:57 UTC (permalink / raw)
  To: bhelgaas, gregkh, rafael, ojeda, alex.gaynor, boqun.feng, gary,
	bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, tmgross
  Cc: linux-pci, rust-for-linux, linux-kernel, Danilo Krummrich

Implement bus_type_raw(), which returns a raw pointer to the device'
struct bus_type.

This is useful for bus devices, to implement the following trait.

	impl TryFrom<&Device> for &pci::Device

With this a caller can try to get the bus specific device from a generic
device in a safe way. try_from() will only succeed if the generic
device' bus type pointer matches the pointer of the bus' type.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/device.rs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 21b343a1dc4d..67a2fc46cf4c 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -65,6 +65,16 @@ pub(crate) fn as_raw(&self) -> *mut bindings::device {
         self.0.get()
     }
 
+    /// Returns a raw pointer to the device' bus type.
+    #[expect(unused)]
+    pub(crate) fn bus_type_raw(&self) -> *const bindings::bus_type {
+        // SAFETY:
+        // - By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
+        // - `dev->bus` is a pointer to a `const struct bus_type`, which is only ever set at device
+        //    creation.
+        unsafe { (*self.as_raw()).bus }
+    }
+
     /// Convert a raw C `struct device` pointer to a `&'a Device`.
     ///
     /// # Safety
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v3 2/3] rust: pci: impl TryFrom<&Device> for &pci::Device
  2025-03-21 14:57 [PATCH v3 0/3] Implement TryFrom<&Device> for bus specific devices Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 1/3] rust: device: implement bus_type_raw() Danilo Krummrich
@ 2025-03-21 14:57 ` Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 3/3] rust: platform: impl TryFrom<&Device> for &platform::Device Danilo Krummrich
  2 siblings, 0 replies; 4+ messages in thread
From: Danilo Krummrich @ 2025-03-21 14:57 UTC (permalink / raw)
  To: bhelgaas, gregkh, rafael, ojeda, alex.gaynor, boqun.feng, gary,
	bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, tmgross
  Cc: linux-pci, rust-for-linux, linux-kernel, Danilo Krummrich

Implement TryFrom<&device::Device> for &Device.

This allows us to get a &pci::Device from a generic &Device in a safe
way; the conversion fails if the device' bus type does not match with
the PCI bus type.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/device.rs |  1 -
 rust/kernel/pci.rs    | 22 ++++++++++++++++++++--
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 67a2fc46cf4c..0bbc67edd38d 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -66,7 +66,6 @@ pub(crate) fn as_raw(&self) -> *mut bindings::device {
     }
 
     /// Returns a raw pointer to the device' bus type.
-    #[expect(unused)]
     pub(crate) fn bus_type_raw(&self) -> *const bindings::bus_type {
         // SAFETY:
         // - By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 22a32172b108..c563129d29b6 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -6,7 +6,7 @@
 
 use crate::{
     alloc::flags::*,
-    bindings, device,
+    bindings, container_of, device,
     device_id::RawDeviceId,
     devres::Devres,
     driver,
@@ -20,7 +20,7 @@
 use core::{
     marker::PhantomData,
     ops::Deref,
-    ptr::{addr_of_mut, NonNull},
+    ptr::{addr_of, addr_of_mut, NonNull},
 };
 use kernel::prelude::*;
 
@@ -466,6 +466,24 @@ fn as_ref(&self) -> &device::Device {
     }
 }
 
+impl TryFrom<&device::Device> for &Device {
+    type Error = kernel::error::Error;
+
+    fn try_from(dev: &device::Device) -> Result<Self, Self::Error> {
+        if dev.bus_type_raw() != addr_of!(bindings::pci_bus_type) {
+            return Err(EINVAL);
+        }
+
+        // SAFETY: We've just verified that the bus type of `dev` equals `bindings::pci_bus_type`,
+        // hence `dev` must be embedded in a valid `struct pci_dev` as guaranteed by the
+        // corresponding C code.
+        let pdev = unsafe { container_of!(dev.as_raw(), bindings::pci_dev, dev) };
+
+        // SAFETY: `pdev` is a valid pointer to a `struct pci_dev`.
+        Ok(unsafe { &*pdev.cast() })
+    }
+}
+
 // SAFETY: A `Device` is always reference-counted and can be released from any thread.
 unsafe impl Send for Device {}
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v3 3/3] rust: platform: impl TryFrom<&Device> for &platform::Device
  2025-03-21 14:57 [PATCH v3 0/3] Implement TryFrom<&Device> for bus specific devices Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 1/3] rust: device: implement bus_type_raw() Danilo Krummrich
  2025-03-21 14:57 ` [PATCH v3 2/3] rust: pci: impl TryFrom<&Device> for &pci::Device Danilo Krummrich
@ 2025-03-21 14:57 ` Danilo Krummrich
  2 siblings, 0 replies; 4+ messages in thread
From: Danilo Krummrich @ 2025-03-21 14:57 UTC (permalink / raw)
  To: bhelgaas, gregkh, rafael, ojeda, alex.gaynor, boqun.feng, gary,
	bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, tmgross
  Cc: linux-pci, rust-for-linux, linux-kernel, Danilo Krummrich

Implement TryFrom<&device::Device> for &Device.

This allows us to get a &platform::Device from a generic &Device in a safe
way; the conversion fails if the device' bus type does not match with
the platform bus type.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/platform.rs | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index e37531bae8e9..d7796967cffa 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -5,7 +5,7 @@
 //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
 
 use crate::{
-    bindings, device, driver,
+    bindings, container_of, device, driver,
     error::{to_result, Result},
     of,
     prelude::*,
@@ -17,7 +17,7 @@
 use core::{
     marker::PhantomData,
     ops::Deref,
-    ptr::{addr_of_mut, NonNull},
+    ptr::{addr_of, addr_of_mut, NonNull},
 };
 
 /// An adapter for the registration of platform drivers.
@@ -234,6 +234,24 @@ fn as_ref(&self) -> &device::Device {
     }
 }
 
+impl TryFrom<&device::Device> for &Device {
+    type Error = kernel::error::Error;
+
+    fn try_from(dev: &device::Device) -> Result<Self, Self::Error> {
+        if dev.bus_type_raw() != addr_of!(bindings::platform_bus_type) {
+            return Err(EINVAL);
+        }
+
+        // SAFETY: We've just verified that the bus type of `dev` equals
+        // `bindings::platform_bus_type`, hence `dev` must be embedded in a valid
+        // `struct platform_device` as guaranteed by the corresponding C code.
+        let pdev = unsafe { container_of!(dev.as_raw(), bindings::platform_device, dev) };
+
+        // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
+        Ok(unsafe { &*pdev.cast() })
+    }
+}
+
 // SAFETY: A `Device` is always reference-counted and can be released from any thread.
 unsafe impl Send for Device {}
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-03-21 14:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-21 14:57 [PATCH v3 0/3] Implement TryFrom<&Device> for bus specific devices Danilo Krummrich
2025-03-21 14:57 ` [PATCH v3 1/3] rust: device: implement bus_type_raw() Danilo Krummrich
2025-03-21 14:57 ` [PATCH v3 2/3] rust: pci: impl TryFrom<&Device> for &pci::Device Danilo Krummrich
2025-03-21 14:57 ` [PATCH v3 3/3] rust: platform: impl TryFrom<&Device> for &platform::Device Danilo Krummrich

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).