The documentation for the generic Device type is outdated and deserves
much more detail.
Hence, expand the documentation and cover topics such as device types,
device contexts, as well as information on how to use the generic device
infrastructure to implement bus and class specific device types.
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/device.rs | 139 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 126 insertions(+), 13 deletions(-)
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index f5d1db568f00..9cc35ea6db98 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -15,23 +15,130 @@
pub mod property;
-/// A reference-counted device.
+/// The core representation of a device in the kernel's driver model.
///
-/// This structure represents the Rust abstraction for a C `struct device`. This implementation
-/// abstracts the usage of an already existing C `struct device` within Rust code that we get
-/// passed from the C side.
+/// This structure represents the Rust abstraction for a C `struct device`. A [`Device`] can either
+/// exist as temporary reference (see also [`Device::from_raw`]), which is only valid within a
+/// certain scope or as [`ARef<Device>`], owning a dedicated reference count.
///
-/// An instance of this abstraction can be obtained temporarily or permanent.
+/// # Device Types
///
-/// A temporary one is bound to the lifetime of the C `struct device` pointer used for creation.
-/// A permanent instance is always reference-counted and hence not restricted by any lifetime
-/// boundaries.
+/// A [`Device`] can represent either a bus device or a class device.
///
-/// For subsystems it is recommended to create a permanent instance to wrap into a subsystem
-/// specific device structure (e.g. `pci::Device`). This is useful for passing it to drivers in
-/// `T::probe()`, such that a driver can store the `ARef<Device>` (equivalent to storing a
-/// `struct device` pointer in a C driver) for arbitrary purposes, e.g. allocating DMA coherent
-/// memory.
+/// ## Bus Devices
+///
+/// A bus device is a [`Device`] that is associated with a physical or virtual bus. Examples of
+/// buses include PCI, USB, I2C, and SPI. Devices attached to a bus are registered with a specific
+/// bus type, which facilitates matching devices with appropriate drivers based on IDs or other
+/// identifying information. Bus devices are visible in sysfs under `/sys/bus/<bus-name>/devices/`.
+///
+/// ## Class Devices
+///
+/// A class device is a [`Device`] that is associated with a logical category of functionality
+/// rather than a physical bus. Examples of classes include block devices, network interfaces, sound
+/// cards, and input devices. Class devices are grouped under a common class and exposed to
+/// userspace via entries in `/sys/class/<class-name>/`.
+///
+/// # Device Context
+///
+/// [`Device`] references are generic over a [`DeviceContext`], which represents the type state of
+/// a [`Device`].
+///
+/// As the name indicates, this type state represents the context of the scope the [`Device`]
+/// reference is valid in. For instance, the [`Bound`] context guarantees that the [`Device`] is
+/// bound to a driver for the entire duration of the existence of a [`Device<Bound>`] reference.
+///
+/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`] and [`CoreInternal`].
+///
+/// Unless selected otherwise [`Device`] defaults to the [`Normal`] [`DeviceContext`], which by
+/// itself has no additional requirements.
+///
+/// It is always up to the caller of [`Device::from_raw`] to select the correct [`DeviceContext`]
+/// type for the corresponding scope the [`Device`] reference is created in.
+///
+/// All [`DeviceContext`] types other than [`Normal`] are intended to be used with
+/// [bus devices](#bus-devices) only.
+///
+/// # Implementing Bus Devices
+///
+/// This section provides a guideline to implement bus specific devices, such as [`pci::Device`] or
+/// [`platform::Device`].
+///
+/// A bus specific device should be defined as follows.
+///
+/// ```ignore
+/// #[repr(transparent)]
+/// pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+/// Opaque<bindings::bus_device_type>,
+/// PhantomData<Ctx>,
+/// );
+/// ```
+///
+/// Since devices are reference counted, [`AlwaysRefCounted`] should be implemented for `Device`
+/// (i.e. `Device<Normal>`). Note that [`AlwaysRefCounted`] must not be implemented for any other
+/// [`DeviceContext`], since all other device context types are only valid in a certain scope.
+///
+/// In order to be able to implement the [`DeviceContext`] dereference hierarchy, bus device
+/// implementations should call the [`impl_device_context_deref`] macro as shown below.
+///
+/// ```ignore
+/// // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s
+/// // generic argument.
+/// kernel::impl_device_context_deref!(unsafe { Device });
+/// ```
+/// In order to convert from a any [`Device<Ctx>`] to [`ARef<Device>`], bus devices can implement
+/// the following macro call.
+///
+/// ```ignore
+/// kernel::impl_device_context_into_aref!(Device);
+/// ```
+/// Bus devices should also implement the following [`AsRef`] implementation, such that users can
+/// easily derive a generic [`Device`] reference.
+///
+/// ```ignore
+/// impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+/// fn as_ref(&self) -> &device::Device<Ctx> {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// # Implementing Class Devices
+///
+/// Class device implementations require less infrastructure and depend slightly more on the
+/// specific subsystem.
+///
+/// An example implementation for a class device could look like this.
+///
+/// ```ignore
+/// #[repr(C)]
+/// #[pin_data]
+/// pub struct Device<T: class::Driver> {
+/// dev: Opaque<bindings::class_device_type>,
+/// #[pin]
+/// data: T::Data,
+/// }
+/// ```
+///
+/// This class device uses the sub-classing pattern to embed the driver's private data within the
+/// allocation of the class device. For this to be possible the class device is generic over the
+/// class specific `Driver` trait implementation.
+///
+/// Just like any device, class devices are reference counted and should hence implement
+/// [`AlwaysRefCounted`] for `Device`.
+///
+/// Class devices should also implement the following [`AsRef`] implementation, such that users can
+/// easily derive a generic [`Device`] reference.
+///
+/// ```ignore
+/// impl<T: class::Driver> AsRef<device::Device> for Device<T> {
+/// fn as_ref(&self) -> &device::Device {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// An example for a class device implementation is [`drm::Device`].
///
/// # Invariants
///
@@ -42,6 +149,12 @@
///
/// `bindings::device::release` is valid to be called from any thread, hence `ARef<Device>` can be
/// dropped from any thread.
+///
+/// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted
+/// [`drm::Device`]: kernel::drm::Device
+/// [`impl_device_context_deref`]: kernel::impl_device_context_deref
+/// [`pci::Device`]: kernel::pci::Device
+/// [`platform::Device`]: kernel::platform::Device
#[repr(transparent)]
pub struct Device<Ctx: DeviceContext = Normal>(Opaque<bindings::device>, PhantomData<Ctx>);
--
2.50.0
On Wed Jul 23, 2025 at 12:00 AM JST, Danilo Krummrich wrote: > The documentation for the generic Device type is outdated and deserves > much more detail. > > Hence, expand the documentation and cover topics such as device types, > device contexts, as well as information on how to use the generic device > infrastructure to implement bus and class specific device types. > > Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> > Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> > Signed-off-by: Danilo Krummrich <dakr@kernel.org> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> <snip> > +/// # Implementing Bus Devices > +/// > +/// This section provides a guideline to implement bus specific devices, such as [`pci::Device`] or > +/// [`platform::Device`]. > +/// > +/// A bus specific device should be defined as follows. > +/// > +/// ```ignore > +/// #[repr(transparent)] > +/// pub struct Device<Ctx: device::DeviceContext = device::Normal>( > +/// Opaque<bindings::bus_device_type>, > +/// PhantomData<Ctx>, > +/// ); > +/// ``` > +/// > +/// Since devices are reference counted, [`AlwaysRefCounted`] should be implemented for `Device` > +/// (i.e. `Device<Normal>`). Note that [`AlwaysRefCounted`] must not be implemented for any other > +/// [`DeviceContext`], since all other device context types are only valid in a certain scope. supernit: "valid within a certain scope"? > +/// > +/// In order to be able to implement the [`DeviceContext`] dereference hierarchy, bus device > +/// implementations should call the [`impl_device_context_deref`] macro as shown below. > +/// > +/// ```ignore > +/// // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s > +/// // generic argument. > +/// kernel::impl_device_context_deref!(unsafe { Device }); > +/// ``` > +/// In order to convert from a any [`Device<Ctx>`] to [`ARef<Device>`], bus devices can implement Missing empty line after the code block? I don't think this would break rendering though. > +/// the following macro call. > +/// > +/// ```ignore > +/// kernel::impl_device_context_into_aref!(Device); > +/// ``` > +/// Bus devices should also implement the following [`AsRef`] implementation, such that users can Here as well.
On Tue, Jul 22, 2025 at 05:00:00PM +0200, Danilo Krummrich wrote: > The documentation for the generic Device type is outdated and deserves > much more detail. > > Hence, expand the documentation and cover topics such as device types, > device contexts, as well as information on how to use the generic device > infrastructure to implement bus and class specific device types. > > Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> > Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> > Signed-off-by: Danilo Krummrich <dakr@kernel.org> A few nits below, but in general looks good. Reviewed-by: Alice Ryhl <aliceryhl@google.com> > -/// This structure represents the Rust abstraction for a C `struct device`. This implementation > -/// abstracts the usage of an already existing C `struct device` within Rust code that we get > -/// passed from the C side. > +/// This structure represents the Rust abstraction for a C `struct device`. A [`Device`] can either > +/// exist as temporary reference (see also [`Device::from_raw`]), which is only valid within a > +/// certain scope or as [`ARef<Device>`], owning a dedicated reference count. Doesn't there need to be a comma between "scope" and "or"? It's possible that I'm confusing the danish and english comma rules, but I got confused when reading this. > +/// # Implementing Class Devices > +/// > +/// Class device implementations require less infrastructure and depend slightly more on the > +/// specific subsystem. > +/// > +/// An example implementation for a class device could look like this. > +/// > +/// ```ignore > +/// #[repr(C)] > +/// #[pin_data] > +/// pub struct Device<T: class::Driver> { > +/// dev: Opaque<bindings::class_device_type>, > +/// #[pin] > +/// data: T::Data, Should the `dev` field not also be pinned? Alice
On Thu Jul 24, 2025 at 9:03 AM CEST, Alice Ryhl wrote: > On Tue, Jul 22, 2025 at 05:00:00PM +0200, Danilo Krummrich wrote: >> The documentation for the generic Device type is outdated and deserves >> much more detail. >> >> Hence, expand the documentation and cover topics such as device types, >> device contexts, as well as information on how to use the generic device >> infrastructure to implement bus and class specific device types. >> >> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> >> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> >> Signed-off-by: Danilo Krummrich <dakr@kernel.org> > > A few nits below, but in general looks good. > > Reviewed-by: Alice Ryhl <aliceryhl@google.com> > >> -/// This structure represents the Rust abstraction for a C `struct device`. This implementation >> -/// abstracts the usage of an already existing C `struct device` within Rust code that we get >> -/// passed from the C side. >> +/// This structure represents the Rust abstraction for a C `struct device`. A [`Device`] can either >> +/// exist as temporary reference (see also [`Device::from_raw`]), which is only valid within a >> +/// certain scope or as [`ARef<Device>`], owning a dedicated reference count. > > Doesn't there need to be a comma between "scope" and "or"? > > It's possible that I'm confusing the danish and english comma rules, but > I got confused when reading this. No, I think you're right. >> +/// # Implementing Class Devices >> +/// >> +/// Class device implementations require less infrastructure and depend slightly more on the >> +/// specific subsystem. >> +/// >> +/// An example implementation for a class device could look like this. >> +/// >> +/// ```ignore >> +/// #[repr(C)] >> +/// #[pin_data] >> +/// pub struct Device<T: class::Driver> { >> +/// dev: Opaque<bindings::class_device_type>, >> +/// #[pin] >> +/// data: T::Data, > > Should the `dev` field not also be pinned? I think we should just remove any pin stuff from the example, that's an implementation detail. -- In case you're curious, the reason it's there is because that's how drm::Device is defined. However, drm::Device doesn't need the pin stuff either, but I forgot to remove it. See drm::Device::new(): /// Create a new `drm::Device` for a `drm::Driver`. pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> { // SAFETY: // - `VTABLE`, as a `const` is pinned to the read-only section of the compilation, // - `dev` is valid by its type invarants, let raw_drm: *mut Self = unsafe { bindings::__drm_dev_alloc( dev.as_raw(), &Self::VTABLE, mem::size_of::<Self>(), mem::offset_of!(Self, dev), ) } .cast(); let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?; // SAFETY: `raw_drm` is a valid pointer to `Self`. let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) }; // SAFETY: // - `raw_data` is a valid pointer to uninitialized memory. // - `raw_data` will not move until it is dropped. unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| { // SAFETY: `__drm_dev_alloc()` was successful, hence `raw_drm` must be valid and the // refcount must be non-zero. unsafe { bindings::drm_dev_put(ptr::addr_of_mut!((*raw_drm.as_ptr()).dev).cast()) }; })?; // SAFETY: The reference count is one, and now we take ownership of that reference as a // `drm::Device`. Ok(unsafe { ARef::from_raw(raw_drm) }) } While we use data.__pinned_init(), I don't think the drm::Device needs any of the pin macros. - Danilo
© 2016 - 2025 Red Hat, Inc.