Rust for Linux List
 help / color / mirror / Atom feed
From: Mike Lothian <mike@fireburn.co.uk>
To: rust-for-linux@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Lyude Paul" <lyude@redhat.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	linux-kernel@vger.kernel.org,
	"Mike Lothian" <mike@fireburn.co.uk>
Subject: [RFC PATCH v2 03/18] rust: drm: kms: break the Driver* trait well-formedness cycle
Date: Fri,  3 Jul 2026 04:00:50 +0100	[thread overview]
Message-ID: <20260703030123.2814-4-mike@fireburn.co.uk> (raw)
In-Reply-To: <20260703030123.2814-1-mike@fireburn.co.uk>

The ported mode-object trait web (KmsDriver / DriverConnector /
DriverConnectorState / VblankImpl, ...) is mutually recursive: a
Modesettable* impl for Crtc<T::Crtc> needs <T::Crtc::State>::Crtc:
DriverCrtc, which needs <T::Crtc::State::Crtc::State>::Crtc, and so on
-- an infinite projection regress the trait solver can't terminate on
unless the associated-state types are pinned to State<Assoc = Self>.

Pin them, add the where-clauses the *StateMutator Deref/DerefMut impls
now need once callers pass through the pinned types, and fix a
from_opaque copy-paste bug in CrtcStateMutator that was reading Plane's
fields instead of Crtc's.

This still doesn't build clean: pinning trades the projection regress
for a coinductive cycle in the from_opaque-on-state impls, which the
current (non-next-gen) trait solver exhausts memory on instead of
resolving. See the following commit.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/kernel/drm/kms.rs           | 28 +++++++++++++---------------
 rust/kernel/drm/kms/connector.rs | 27 ++++++++++++++++++++-------
 rust/kernel/drm/kms/crtc.rs      | 31 +++++++++++++++++++++----------
 rust/kernel/drm/kms/plane.rs     | 27 ++++++++++++++++++++-------
 rust/kernel/drm/kms/vblank.rs    |  5 +++--
 5 files changed, 77 insertions(+), 41 deletions(-)

diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index a147276b3412..d07389fd510c 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -122,25 +122,23 @@ fn deref(&self) -> &Self::Target {
 /// [`PhantomData<Self>`]: PhantomData
 #[vtable]
 pub trait KmsDriver: Driver {
-    /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverConnector` implementations.
-    type Connector: connector::DriverConnector<Driver = Self>;
+    // The driver's mode-object implementations. Left *unbounded* to break a trait
+    // well-formedness cycle: the natural bound (`type Crtc: DriverCrtc<Driver = Self>`, etc.)
+    // makes every `T: KmsDriver` obligation pull in the mutually-recursive Driver* web, which
+    // the compiler cannot evaluate (it loops in obligation processing and exhausts memory). The
+    // `DriverConnector`/`DriverCrtc`/... bounds are required at the use sites as `where`-clauses
+    // instead (an assumption, not an eagerly WF-checked trait-definition obligation).
+    //
+    // TODO: unneeded once we support multiple `Driver*` implementations per driver.
 
+    /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
+    type Connector;
     /// The driver's [`DriverPlane`](plane::DriverPlane) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverPlane` implementations.
-    type Plane: plane::DriverPlane<Driver = Self>;
-
+    type Plane;
     /// The driver's [`DriverCrtc`](crtc::DriverCrtc) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverCrtc` implementations.
-    type Crtc: crtc::DriverCrtc<Driver = Self>;
-
+    type Crtc;
     /// The driver's [`DriverEncoder`](encoder::DriverEncoder) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverEncoder` implementations.
-    type Encoder: encoder::DriverEncoder<Driver = Self>;
+    type Encoder;
 
     /// Return a [`ModeConfigInfo`] structure for this [`device::Device`].
     fn mode_config_info(dev: &Device<Self, Uninit>) -> Result<ModeConfigInfo>
diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index 05ec64cf6fa2..aca7be50e583 100644
--- a/rust/kernel/drm/kms/connector.rs
+++ b/rust/kernel/drm/kms/connector.rs
@@ -139,7 +139,7 @@ pub trait DriverConnector: Send + Sync + Sized {
     /// The [`DriverConnectorState`] implementation for this [`DriverConnector`].
     ///
     /// See [`DriverConnectorState`] for more info.
-    type State: DriverConnectorState;
+    type State: DriverConnectorState<Connector = Self>;
 
     /// The constructor for creating a [`Connector`] using this [`DriverConnector`] implementation.
     ///
@@ -700,13 +700,17 @@ pub struct ConnectorState<T: DriverConnectorState> {
 /// [`struct drm_connector`]: srctree/include/drm_connector.h
 /// [`struct drm_connector_state`]: srctree/include/drm_connector.h
 pub trait DriverConnectorState: Clone + Default + Sized {
-    /// The parent [`DriverConnector`].
-    type Connector: DriverConnector;
+    /// The parent [`DriverConnector`]. Unbounded to break the trait WF cycle; the
+    /// `DriverConnector` bound is required at use sites instead.
+    type Connector;
 }
 
 impl<T: DriverConnectorState> Sealed for ConnectorState<T> {}
 
-impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T> {
+impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T>
+where
+    T::Connector: DriverConnector,
+{
     type Connector = Connector<T::Connector>;
 }
 
@@ -720,7 +724,10 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state {
     }
 }
 
-impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T> {
+impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T>
+where
+    T::Connector: DriverConnector,
+{
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self {
         // Our data layout starts with `bindings::drm_connector_state`.
         let ptr: *const Self = ptr.cast();
@@ -859,7 +866,10 @@ pub(super) fn new<D: KmsDriver>(
     }
 }
 
-impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>> {
+impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>>
+where
+    T::Connector: DriverConnector,
+{
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -867,7 +877,10 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>> {
+impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>>
+where
+    T::Connector: DriverConnector,
+{
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.state.inner
     }
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index 0f579dc4ec75..4c6cb6bbdcb8 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -124,7 +124,7 @@ pub trait DriverCrtc: Send + Sync + Sized {
     /// The [`DriverCrtcState`] implementation for this [`DriverCrtc`].
     ///
     /// See [`DriverCrtcState`] for more info.
-    type State: DriverCrtcState;
+    type State: DriverCrtcState<Crtc = Self>;
 
     /// The driver's optional hardware vblank implementation
     ///
@@ -561,10 +561,9 @@ impl<T: DriverCrtcState> Sealed for CrtcState<T> {}
 /// [`struct drm_crtc`]: srctree/include/drm_crtc.h
 /// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h
 pub trait DriverCrtcState: Clone + Default + Unpin {
-    /// The parent CRTC driver for this CRTC state
-    type Crtc: DriverCrtc<State = Self>
-    where
-        Self: Sized;
+    /// The parent CRTC driver for this CRTC state. Unbounded to break the trait WF cycle;
+    /// the `DriverCrtc` bound is required at use sites instead.
+    type Crtc;
 }
 
 /// The main interface for a [`struct drm_crtc_state`].
@@ -702,11 +701,17 @@ fn as_raw(&self) -> *mut bindings::drm_crtc_state {
     }
 }
 
-impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T> {
+impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T>
+where
+    T::Crtc: DriverCrtc,
+{
     type Crtc = Crtc<T::Crtc>;
 }
 
-impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T> {
+impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T>
+where
+    T::Crtc: DriverCrtc,
+{
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self {
         // SAFETY: Our data layout starts with `bindings::drm_crtc_state`
         unsafe { &*(ptr.cast()) }
@@ -800,7 +805,7 @@ fn <D, C>(CrtcStateMutator<'a, OpaqueCrtcState<D>>) -> Self
         where
             T: DriverCrtcState<Crtc = C>;
         use
-            T as DriverCrtc,
+            C as DriverCrtc,
             D as KmsDriver<Crtc = ...>
     }
 }
@@ -813,7 +818,10 @@ fn drop(&mut self) {
     }
 }
 
-impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>> {
+impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>>
+where
+    T::Crtc: DriverCrtc,
+{
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -823,7 +831,10 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>> {
+impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>>
+where
+    T::Crtc: DriverCrtc,
+{
     fn deref_mut(&mut self) -> &mut Self::Target {
         // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules,
         // so this is safe
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 6f547adcfdc9..47e05fe6bee1 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -98,7 +98,7 @@ pub trait DriverPlane: Send + Sync + Sized {
     /// The [`DriverPlaneState`] implementation for this [`DriverPlane`].
     ///
     /// See [`DriverPlaneState`] for more info.
-    type State: DriverPlaneState;
+    type State: DriverPlaneState<Plane = Self>;
 
     /// The constructor for creating a [`Plane`] using this [`DriverPlane`] implementation.
     ///
@@ -689,13 +689,17 @@ pub struct PlaneState<T: DriverPlaneState> {
 /// [`struct drm_plane`]: srctree/include/drm_plane.h
 /// [`struct drm_plane_state`]: srctree/include/drm_plane.h
 pub trait DriverPlaneState: Clone + Default + Sized {
-    /// The type for this driver's drm_plane implementation
-    type Plane: DriverPlane;
+    /// The type for this driver's drm_plane implementation. Unbounded to break the trait WF
+    /// cycle; the `DriverPlane` bound is required at use sites instead.
+    type Plane;
 }
 
 impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
 
-impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T> {
+impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T>
+where
+    T::Plane: DriverPlane,
+{
     type Plane = Plane<T::Plane>;
 }
 
@@ -709,7 +713,10 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
     }
 }
 
-impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T> {
+impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T>
+where
+    T::Plane: DriverPlane,
+{
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
         // Our data layout starts with `bindings::drm_plane_state`.
         let ptr: *const Self = ptr.cast();
@@ -885,7 +892,10 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
 
 impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T> {}
 
-impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>> {
+impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>>
+where
+    T::Plane: DriverPlane,
+{
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -893,7 +903,10 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>> {
+impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>>
+where
+    T::Plane: DriverPlane,
+{
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.state.inner
     }
diff --git a/rust/kernel/drm/kms/vblank.rs b/rust/kernel/drm/kms/vblank.rs
index dc34e02e8ccb..534fd18f63b4 100644
--- a/rust/kernel/drm/kms/vblank.rs
+++ b/rust/kernel/drm/kms/vblank.rs
@@ -62,8 +62,9 @@ fn get_vblank_timestamp(
 /// Drivers interested in implementing vblank support should refer to [`VblankSupport`], drivers
 /// that don't have vblank support can use [`PhantomData`].
 pub trait VblankImpl {
-    /// The parent [`DriverCrtc`].
-    type Crtc: DriverCrtc<VblankImpl = Self>;
+    /// The parent [`DriverCrtc`]. Unbounded to break the trait WF cycle; the `DriverCrtc`
+    /// bound is required at use sites instead.
+    type Crtc;
 
     /// The generated [`VblankOps`].
     const VBLANK_OPS: VblankOps;
-- 
2.55.0


  parent reply	other threads:[~2026-07-03  3:01 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 1/4] rust: drm: add minimal KMS bindings for simple-display-pipe drivers Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 2/4] rust: drm: expose drm_edid.h for reading connector EDID Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 3/4] rust: drm: expose drm::Device::as_raw() Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 4/4] rust: drm: expose drm_blend.h and the atomic new-CRTC-state accessor Mike Lothian
2026-06-17 15:11 ` [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Miguel Ojeda
2026-06-17 15:29   ` Mike Lothian
2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
2026-07-07 21:46     ` lyude
2026-07-07 22:21     ` lyude
2026-07-03  3:00   ` [RFC PATCH v2 02/18] rust: drm: kms: adapt the port to current drm-next Mike Lothian
2026-07-03  3:00   ` Mike Lothian [this message]
2026-07-03  3:00   ` [RFC PATCH v2 04/18] rust: drm: kms: build the kernel crate clean under -Znext-solver Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 05/18] rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message definitions Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard Mike Lothian
2026-07-07 21:51     ` lyude
2026-07-03  3:00   ` [RFC PATCH v2 07/18] rust: drm: kms: add safe accessors for common state and connector modes Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 08/18] rust: drm: tyr: add the Kms associated type Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 09/18] rust: drm: add drm_event delivery Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 10/18] rust: drm: allow drivers to declare ioctls from their own uAPI module Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 11/18] rust: platform: add runtime platform device creation Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 12/18] rust: drm: framebuffer: add geometry accessors, refcounting and a byte-slice vmap Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 13/18] rust: i2c: add adapter-provider (bus controller) registration Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 14/18] rust: add sysfs device attribute groups Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 15/18] rust: drm: support hardware cursor planes with sleepable event delivery Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 16/18] rust: drm: add CRTC gamma LUT and plane rotation property bindings Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 17/18] rust: drm: kms: add connector detect() and mode_valid() hooks Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 18/18] rust: drm: kms: add plane damage-clip accessors Mike Lothian

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=20260703030123.2814-4-mike@fireburn.co.uk \
    --to=mike@fireburn.co.uk \
    --cc=a.hindborg@kernel.org \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tmgross@umich.edu \
    /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