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
next prev 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