| Reviewer | None |
|---|---|
| Submitted | March 22, 2024, 10:03 p.m. |
| Last Updated | March 5, 2025, 11:10 p.m. |
| Revision | 3 |
| Root msg-id(s): |
20240322221305.1403600-1-lyude@redhat.com 20240930233257.1189730-1-lyude@redhat.com 20250305230406.567126-1-lyude@redhat.com |
Hi again! It's been a while since the last time I sent this, there's
still a good bit of work to do here but I think there's more then enough
to start reviewing the design I have so far :) - especially since I'll
be presenting this work at XDC2024 this year. This patch series
introduces a WIP set of bindings for KMS drivers written in rust, based
on top of the work of quite a number of people:
* Garry Guo's #[unique] macro for #[vtable]
(used for getting consistent memory addresses for C vtables, which we
need for Opaque* object types)
* Andreas Hindborg's hrtimer bindings
For vblank emulation in rvkms. Note: the version of the patch series
used here is slightly older then the one he last sent upstream, but
API wise it's more or less identical, with some additions I need to
upstream.
* My IRQ bindings for rust + SpinlockIrq type
* Misc. Lock additions from me that need to be cleaned up + upstreamed
* Asahi Lina and MarĂa Canal's platform driver bindings + resource
management patches
I need to clean these up quite a bit and work on upstreaming these
* Asahi Lina and Danilo Krummrich's DRM device bindings for rust
* Asahi Lina's gem shmem bindings
* Some misc. DRM fixes from me
All of these dependencies are either in the process of currently being
upstreamed, or are planned by me to be upstreamed.
Since this is still a WIP, I've done my best to mark all of the patches
where I think there's still work to be done - along with leaving TODOs
in various comments, and in the commit descriptions for each WIP patch.
Some general TODOs series-wide to keep in mind here:
* I don't have code examples in the documentation yet, consider rvkms to
be that example for the time being
* This compiles with a lot of warnings. I will hopefully have these
cleaned up soon, but didn't have the time to sort through all of them
since some of them are leftover from various dependencies we have
* Most of the documentation has been typed up, but don't be surprised if
you find a few formatting issues (feel free to point them out though!)
* I need to go through and add appropriate SPDX copyright notices
* I need to make sure these compile independently. I think they should,
but it's been a while since I checked
* I've left some currently unused bindings out, including:
* CRTC commit_data equivalents
* "Ephemeral data" - e.g. data in Crtc, Plane, and Connector objects
that is embedded within the main modeset objects themselves but can
only be accessed during an atomic commit.
* Misc. DRM helpers (currently just a drm_rect port to rust)
* I still need to make the device registration in rvkms static,
currently we do device probing/creation in the legacy fashion.
Because of the pretty substantial number of dependencies this patch
series relies on, I currently have a tag for this on my freedesktop
branch:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rvkms-xdc2024-base
Additionally, you can see the rest of the work I've done so far
(including the patches I omitted for this series) here:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rvkms-wip
And finally, I do have these patches applied on a branch also available
on my gitlab:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rvkms-xdc2024
And of course - since the last time I sent out these patches, I've split
things up quite a bit to make it easier to go through.
Cheers!
Lyude Paul (35):
WIP: rust/drm: Add fourcc bindings
WIP: rust: drm: Add traits for registering KMS devices
rust: drm/kms/fbdev: Add FbdevShmem
rust: drm/kms: Introduce the main ModeConfigObject traits
rust: drm/kms: Add bindings for drm_connector
rust: drm/kms: Add drm_plane bindings
WIP: rust: drm/kms: Add drm_crtc bindings
rust: drm/kms: Add bindings for drm_encoder
WIP: rust: drm/kms: Add Connector.attach_encoder()
rust: drm/kms: Add DriverConnector::get_mode callback
rust: drm/kms: Add ConnectorGuard::add_modes_noedid()
rust: drm/kms: Add ConnectorGuard::set_preferred_mode
WIP: rust: drm/kms: Add OpaqueConnector and OpaqueConnectorState
WIP: rust: drm/kms: Add OpaqueCrtc and OpaqueCrtcState
WIP: rust: drm/kms: Add OpaquePlane and OpaquePlaneState
rust: drm/kms: Add RawConnector and RawConnectorState
rust: drm/kms: Add RawCrtc and RawCrtcState
rust: drm/kms: Add RawPlane and RawPlaneState
WIP: rust: drm/kms: Add OpaqueEncoder
WIP: rust: drm/kms: Add drm_atomic_state bindings
rust: drm/kms: Introduce DriverCrtc::atomic_check()
rust: drm/kms: Add DriverPlane::atomic_update()
rust: drm/kms: Add DriverPlane::atomic_check()
rust: drm/kms: Add RawCrtcState::active()
rust: drm/kms: Add RawPlaneState::crtc()
WIP: rust: drm/kms: Add RawPlaneState::atomic_helper_check()
rust: drm/kms: Add drm_framebuffer bindings
rust: drm/kms: Add RawPlane::framebuffer()
rust: drm/kms: Add DriverCrtc::atomic_begin() and atomic_flush()
rust: drm/kms: Add DriverCrtc::atomic_enable() and atomic_disable()
rust: drm: Add Device::event_lock()
rust: drm/kms: Add Device::num_crtcs()
WIP: rust: drm/kms: Add VblankSupport
WIP: rust: drm/kms: Add Kms::atomic_commit_tail
WIP: drm: Introduce RVKMS!
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/rvkms/Kconfig | 3 +
drivers/gpu/drm/rvkms/Makefile | 1 +
drivers/gpu/drm/rvkms/connector.rs | 53 ++
drivers/gpu/drm/rvkms/crtc.rs | 253 ++++++++
drivers/gpu/drm/rvkms/encoder.rs | 33 +
drivers/gpu/drm/rvkms/file.rs | 22 +
drivers/gpu/drm/rvkms/gem.rs | 30 +
drivers/gpu/drm/rvkms/output.rs | 55 ++
drivers/gpu/drm/rvkms/plane.rs | 81 +++
drivers/gpu/drm/rvkms/rvkms.rs | 168 +++++
rust/bindings/bindings_helper.h | 11 +
rust/helpers/drm/atomic.c | 32 +
rust/helpers/drm/drm.c | 5 +
rust/helpers/drm/vblank.c | 8 +
rust/kernel/drm/device.rs | 25 +-
rust/kernel/drm/drv.rs | 45 +-
rust/kernel/drm/fourcc.rs | 127 ++++
rust/kernel/drm/kms.rs | 475 +++++++++++++++
rust/kernel/drm/kms/atomic.rs | 774 +++++++++++++++++++++++
rust/kernel/drm/kms/connector.rs | 831 +++++++++++++++++++++++++
rust/kernel/drm/kms/crtc.rs | 944 +++++++++++++++++++++++++++++
rust/kernel/drm/kms/encoder.rs | 303 +++++++++
rust/kernel/drm/kms/fbdev.rs | 51 ++
rust/kernel/drm/kms/fbdev/shmem.rs | 33 +
rust/kernel/drm/kms/framebuffer.rs | 58 ++
rust/kernel/drm/kms/plane.rs | 875 ++++++++++++++++++++++++++
rust/kernel/drm/kms/vblank.rs | 454 ++++++++++++++
rust/kernel/drm/mod.rs | 2 +
30 files changed, 5747 insertions(+), 8 deletions(-)
create mode 100644 drivers/gpu/drm/rvkms/Kconfig
create mode 100644 drivers/gpu/drm/rvkms/Makefile
create mode 100644 drivers/gpu/drm/rvkms/connector.rs
create mode 100644 drivers/gpu/drm/rvkms/crtc.rs
create mode 100644 drivers/gpu/drm/rvkms/encoder.rs
create mode 100644 drivers/gpu/drm/rvkms/file.rs
create mode 100644 drivers/gpu/drm/rvkms/gem.rs
create mode 100644 drivers/gpu/drm/rvkms/output.rs
create mode 100644 drivers/gpu/drm/rvkms/plane.rs
create mode 100644 drivers/gpu/drm/rvkms/rvkms.rs
create mode 100644 rust/helpers/drm/atomic.c
create mode 100644 rust/helpers/drm/vblank.c
create mode 100644 rust/kernel/drm/fourcc.rs
create mode 100644 rust/kernel/drm/kms.rs
create mode 100644 rust/kernel/drm/kms/atomic.rs
create mode 100644 rust/kernel/drm/kms/connector.rs
create mode 100644 rust/kernel/drm/kms/crtc.rs
create mode 100644 rust/kernel/drm/kms/encoder.rs
create mode 100644 rust/kernel/drm/kms/fbdev.rs
create mode 100644 rust/kernel/drm/kms/fbdev/shmem.rs
create mode 100644 rust/kernel/drm/kms/framebuffer.rs
create mode 100644 rust/kernel/drm/kms/plane.rs
create mode 100644 rust/kernel/drm/kms/vblank.rs
Hi! It's been a while but I think I'm quite happy with where this patch
series is at the moment. I've gone through most of the changes suggested
on the mailing list last, and have also gone and done quite a lot of
cleanup. Here's some of the big changes I've made across the patch
series:
* Cleaned up pretty much all of the WIP bits, I think I'm more confident with
the design here now
* Limited the scope of the patches a bit more and removed:
* Plane state iterators (I think people get the idea, we want these but
there's nothing I have to use these just yet)
* Some unused bindings for drm_format_info
* We use the faux driver subsytem now in response to some of Greg's comments
* All DRM types should have functions for converting from Opaque variants, and
we now use a fancy macro for implementing this (this will make adding
iterators in the future super easy as well)
* Lots of documentation cleanups
* Lots of safety comment cleanups
* Ensure type IDs for encoders, connectors, etc. are all fully defined using
more fancy macro
* We now have special types for handling unregistered variants of mode objects,
which fixes the last soundness issue I'm aware of.
There's a lot of other changes that I've noted in each patch as well. It's
entirely possible with how many changes that I missed some feedback along the
way, but I think I did a pretty thorough job of documenting all of the changes I
did made (thanks squash!).
The only thing I think is still up in the air is documentation - I've linked in
a few additional spots back to kernel headers/kernel documentation, but ideally
I would like us to be able to link back to the relevant C function for almost
every binding to try to slim down how much duplicate documentation we maintain.
This patch series still depends on the base branch that I've been maintaining as
we still have a handful of dependencies that I'm working on getting upstream,
you can find the base branch here:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rvkms-base
As well, you can find the previous version of this patch series here:
https://lore.kernel.org/dri-devel/C75763C3-A2A4-410F-934D-582B44A3B550@collabora.com/T/
ALSO!!!!!
Please help review it is ok if you don't know rust, or if you know rust but
don't know DRM. I honestly trust the DRM/rust community enough to know that
y'all will ask questions in earnest and I'm happy to do my best to answer them
:).
Lyude Paul (33):
rust: drm: Add a small handful of fourcc bindings
rust: drm: Add traits for registering KMS devices
rust: drm/kms: Introduce the main ModeConfigObject traits
rust: drm/kms: Add drm_connector bindings
rust: drm/kms: Add drm_plane bindings
rust: drm/kms: Add drm_crtc bindings
rust: drm/kms: Add drm_encoder bindings
rust: drm/kms: Add UnregisteredConnector::attach_encoder()
rust: drm/kms: Add DriverConnector::get_mode callback
rust: drm/kms: Add ConnectorGuard::add_modes_noedid()
rust: drm/kms: Add ConnectorGuard::set_preferred_mode
rust: drm/kms: Add RawConnector and RawConnectorState
rust: drm/kms: Add RawPlane and RawPlaneState
rust: drm/kms: Add OpaqueConnector and OpaqueConnectorState
rust: drm/kms: Add OpaqueCrtc and OpaqueCrtcState
rust: drm/kms: Add OpaquePlane and OpaquePlaneState
rust: drm/kms: Add OpaqueEncoder
rust: drm/kms: Add drm_atomic_state bindings
rust: drm/kms: Add DriverCrtc::atomic_check()
rust: drm/kms: Add DriverPlane::atomic_update()
rust: drm/kms: Add DriverPlane::atomic_check()
rust: drm/kms: Add RawCrtcState::active()
rust: drm/kms: Add RawPlaneState::crtc()
rust: drm/kms: Add RawPlaneState::atomic_helper_check()
rust: drm/kms: Add drm_framebuffer bindings
rust: drm/kms: Add RawPlane::framebuffer()
rust: drm/kms: Add DriverCrtc::atomic_begin() and atomic_flush()
rust: drm/kms: Add DriverCrtc::atomic_enable() and atomic_disable()
rust: drm: Add Device::event_lock()
rust: drm/kms: Add Device::num_crtcs()
rust: drm/kms: Add VblankSupport
rust: drm/kms: Add Kms::atomic_commit_tail
drm: Introduce RVKMS!
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/rvkms/Kconfig | 3 +
drivers/gpu/drm/rvkms/Makefile | 1 +
drivers/gpu/drm/rvkms/connector.rs | 55 ++
drivers/gpu/drm/rvkms/crtc.rs | 245 ++++++
drivers/gpu/drm/rvkms/encoder.rs | 31 +
drivers/gpu/drm/rvkms/file.rs | 19 +
drivers/gpu/drm/rvkms/gem.rs | 29 +
drivers/gpu/drm/rvkms/output.rs | 36 +
drivers/gpu/drm/rvkms/plane.rs | 73 ++
drivers/gpu/drm/rvkms/rvkms.rs | 140 ++++
rust/bindings/bindings_helper.h | 14 +
rust/helpers/drm/atomic.c | 32 +
rust/helpers/drm/drm.c | 5 +
rust/helpers/drm/vblank.c | 8 +
rust/kernel/drm/device.rs | 17 +-
rust/kernel/drm/drv.rs | 56 +-
rust/kernel/drm/fourcc.rs | 20 +
rust/kernel/drm/gem/mod.rs | 4 +
rust/kernel/drm/gem/shmem.rs | 4 +
rust/kernel/drm/kms.rs | 574 ++++++++++++++
rust/kernel/drm/kms/atomic.rs | 717 ++++++++++++++++++
rust/kernel/drm/kms/connector.rs | 1003 +++++++++++++++++++++++++
rust/kernel/drm/kms/crtc.rs | 1109 ++++++++++++++++++++++++++++
rust/kernel/drm/kms/encoder.rs | 413 +++++++++++
rust/kernel/drm/kms/framebuffer.rs | 73 ++
rust/kernel/drm/kms/plane.rs | 1077 +++++++++++++++++++++++++++
rust/kernel/drm/kms/vblank.rs | 448 +++++++++++
rust/kernel/drm/mod.rs | 2 +
30 files changed, 6202 insertions(+), 9 deletions(-)
create mode 100644 drivers/gpu/drm/rvkms/Kconfig
create mode 100644 drivers/gpu/drm/rvkms/Makefile
create mode 100644 drivers/gpu/drm/rvkms/connector.rs
create mode 100644 drivers/gpu/drm/rvkms/crtc.rs
create mode 100644 drivers/gpu/drm/rvkms/encoder.rs
create mode 100644 drivers/gpu/drm/rvkms/file.rs
create mode 100644 drivers/gpu/drm/rvkms/gem.rs
create mode 100644 drivers/gpu/drm/rvkms/output.rs
create mode 100644 drivers/gpu/drm/rvkms/plane.rs
create mode 100644 drivers/gpu/drm/rvkms/rvkms.rs
create mode 100644 rust/helpers/drm/atomic.c
create mode 100644 rust/helpers/drm/vblank.c
create mode 100644 rust/kernel/drm/fourcc.rs
create mode 100644 rust/kernel/drm/kms.rs
create mode 100644 rust/kernel/drm/kms/atomic.rs
create mode 100644 rust/kernel/drm/kms/connector.rs
create mode 100644 rust/kernel/drm/kms/crtc.rs
create mode 100644 rust/kernel/drm/kms/encoder.rs
create mode 100644 rust/kernel/drm/kms/framebuffer.rs
create mode 100644 rust/kernel/drm/kms/plane.rs
create mode 100644 rust/kernel/drm/kms/vblank.rs
| # | Name | Submitter | State | TC | A | F | R | T |
|---|---|---|---|---|---|---|---|---|
| [1/4] WIP: rust: Add basic KMS bindings | Lyude Paul | New | 3 | |||||
| [2/4] WIP: drm: Introduce rvkms | Lyude Paul | New | 4 | |||||
| [3/4] rust/drm/kms: Extract PlaneState<T> into IntoPlaneState | Lyude Paul | New | 0 | |||||
| [4/4] WIP: rust/drm/kms: Add ShadowPlaneState<T> | Lyude Paul | New | 0 | |||||
Hi everyone! I mentioned a little while ago that I've been working on porting vkms over to rust so that we could come up with a set of rust KMS bindings for the nova driver to be able to have a modesetting driver written in rust. This driver currently doesn't really do much, but it does load and register a modesetting device! I wanted to send this early on so that people could take a look and tell me if there's anything I've overlooked so far. As well, I've written up a high level overview of how the interface works - along with my reasoning for a lot of the design choices here. I'm sure things will change a bit as I figure out what we really need while porting rvkms, but I'm hoping this is pretty close to what the final set of bindings are going to look like. Note: not all of the patches required for building this have been included. I've just included the kms/rvkms bits here, but the full branch can be found here: https://gitlab.freedesktop.org/lyudess/linux/-/commits/rvkms-03222024 So, the overview! # Modesetting objects and atomic states A general overview of the interface: so far we have the following traits for drivers to implement: * DriverConnector * DriverCrtc * DriverPlane * DriverEncoder Each one of these modesetting objects also has a typed container that can be used to refer to it by drivers: Connector<T>, Plane<T>, etc. Each typed container has only two members (not counting phantoms). Using Crtc<T> as an example: /// A typed KMS CRTC with a specific driver. #[repr(C)] #[pin_data] pub struct Crtc<T: DriverCrtc> { // The FFI drm_crtc object pub(super) crtc: Opaque<bindings::drm_crtc>, /// The driver's private inner data #[pin] inner: T, #[pin] _p: PhantomPinned, } The first member is an Opaque<> container containing the FFI struct for the modesetting object, and the second `inner` contains the implementing driver's private data for the modesetting object. Each modesetting object container can be coerced (through Deref<>) into an immutable reference to inner to make accessing driver-private data easy. This allows subclassing of modesetting objects in a similar manner to what we currently do in C. The main difference is that modesetting objects are immutable to drivers (as in, drivers can only get immutable references to these objects). The reason for this is that while it's common for drivers to stick their own private data in objects, pretty much all of this data falls into one of these categories: * Static driver data which is assigned during driver init. In our abstraction, this assignment will happen in each modesetting object's T::new() function - where the driver can use each modesetting object trait's Args type to specify a data type containing such data which will be passed to T::new(). This should always be available through immutable references. * Modesetting state for drivers which needs to be kept track of independently of the atomic state. As the name entails, all data in this category is assumed to only be accessible within a modesetting context - which itself ensures synchronization. At some point we will implement a container type for this, which only provides a &mut reference once the caller provides proof that we're in an atomic modesetting context and thus - are the only ones who could possibly hold a mutable reference. * Miscellaneous driver state, which may need to be accessed both inside and outside of the atomic modesetting context. We will provide no container type for this, and it will be up to the driver to provide an appropriate container that ensures synchronization and hands out mutable references as needed. A simple example of such a container would be Mutex<T>. # Opaque modesetting objects This is something I haven't written up code for yet because we haven't run into any use for it, but presumably we will eventually. Basically: since we have driver subclassing through types like Connector<T> which the main DRM device trait doesn't need to know about - it is technically possible for us to have multiple different implementations of a modesetting object (for instance, a driver could have two DriverConnector implementations for different types of display connectors). At first I thought this might be too complicated, but I don't think it is! To support this, we would use a similar solution to what the GEM bindings currently do - by providing an opaque version of each modesetting object which only can be used for operations common across different implementations of that object. This Opaque struct could then be fallibly converted to its more qualified type, and we can ensure type safety here by simply using each implementation's vtable pointer (all of which should be 'static) to tell whether an opaque object matches the qualified type the driver is expecting. Here's some example psuedocode to give you an idea: ``` let drm_dev: drm::device::Device<Driver>; /* initialized elsewhere */ type Plane = drm::kms::plane::Plane<DriverPlane>; for plane in drm_dev.iter_planes().filter_map(Plane::from_opaque) { // ... do something } ``` We'd only really need to use these opaque types in situations where we can't guarantee type invariance, like iterating a list of multiple impls of modesetting objects. So, that more or less just means "anywhere outside of a DRM callback" (callbacks can ensure type invariance since each type has its own vtable). # Atomic states for modesetting objects For objects with atomic states, these traits are also implemented by the driver and allow subclassing as well - but with a few differences: * ConnectorState * CrtcState * PlaneState The main difference is that unlike with modesetting objects, atomic states are initially* considered mutable objects. This is mainly since contexts like atomic_check can satisfy rust's data aliasing rules, and it's just a lot easier for drivers to be able to get &mut references to these easily. It is likely that we may expose these objects later on through Pin<&mut> - since we don't want to allow them to move as certain drivers will do things like storing work structs in their states. I haven't gotten this far in the code yet though! # Helpers VKMS does not actually use drm_plane_state! Instead, it actually uses a GEM provided atomic state helper and the drm_shadow_plane_struct - which contains a drm_plane_state. And this is only a single example of such a helper, as a number of other ones which change the struct type a driver uses exist. So, how do we actually handle this in our bindings? Well, by shamelessly copying what the GEM bindings do! For modesetting objects where we want to also support the use of a helper that comes with it's own modesetting object/atomic state struct, we can follow another trait pattern. Using PlaneState<T> and shadow planes as an example: we can introduce a sealed trait named IntoPlaneState. This trait will represent any object in rust which contains a drm_plane_state - along with providing a set of functions that can be implemented to tell rust how to translate the object into a drm_plane_state, along with providing methods for invoking any other DRM functions that might be different for a helper. In the patch series I've posted, I've included some of the WIP code for handling shadow planes in this manner. # The initially* mutable global atomic state I have some WIP code written up for this, but nothing very complete quite yet as we haven't yet needed to manually instantiate an atomic state yet. But in some of the code I wrote up, I noticed some funny gotchas. The main drm_atomic_state structure is refcounted - which means multiple references to an atomic state can exist at once. This complicates things just a bit - as rust's data aliasing rules only allow for one mutable reference to an object to exist at a time. My current plan for handling this is to extend the kernel's ARef<T> type to also include a UniqueARef<T> type. This is basically the same as the kernel's UniqueArc<T> type, but for already refcounted objects - which will allow a caller mutable access to the underlying object so long as it's the only one holding a ref to it. Sharing references to the atomic state requires will require converting away from the UniqueARef. I think this should be enough, since drivers shouldn't really be making any changes to a state after the atomic check phase - so only having immutable references after that point should be good enough to make everyone happy. # Modesetting object lifetimes, ModesetObject and StaticModesetObject I think sima may have tried to warn me about this, because this was definitely a strange bit to figure out. There are two main types of modesetting objects in the kernel: refcounted modesetting objects, and non-refcounted modesetting objects. Reference counted objects are easy: we just implement the kernel's AlwaysRefCounted trait for them. Non-refcounted modesetting objects are weirder, as they share the lifetime of the device. This was one of the other big reasons we have to always expose modesetting objects through immutable references giving driver ownership over these structs would imply the driver could drop them before the drm device has been dropped which would cause UB. I originally considered having modesetting objects live in Arc<> internally to workaround this, but decided against it as this would mean we could make a non-refcounted modesetting object outlive it's device - which could also cause UB if a driver tried to do a modesetting operation with said object. I'd _really_ love to figure out how to handle this with references and lifetimes, as right now we currently tie the lifetime of a modesetting object's reference to the lifetime of the reference to the DRM device that created it - but this isn't currently enough to store those references in a driver's private data as I can't figure out how to tell the compiler "this reference is valid for as long as you hold an ARef for the DRM device". Which brings us to ModesetObject and StaticModesetObject. ModesetObject is a simple trait implemented by all DRM modesetting objects, which provides access to functions common across them (such as fetching the DRM device associated with a modesetting object). StaticModesetObject is a supertrait of ModesetObject which essentially marks a modesetting object as not having a refcount. We then provide KmsRef<T> which can be used to retrieve a "owned" reference to a modesetting object which can be stored in device state. This owned reference is simply a pointer to the modesetting object, along with an owned refcount for that object's parent device - which should ensure that the modesetting object is initialized and valid for as long as a KmsRef<> is held. I'm open for better ideas to solve this though :). # BFLs Unfortunately KMS has a few BFLs. In particular, drm_device.mode_config.mutex is one such BFL that protects anything related to hotplugging state (and probably a few other things?). In rust, data races are considered UB - so we need to be careful to ensure that any safe bindings we expose take care to be thread safe. Since rust usually represents locks as container types (e.g. Mutex<T>), but we have to deal with a BFL, we need a different solution that we can use to represent critical sections under this BFL. This brings us to ModeConfigGuard and ConnectorGuard. In rust, upon acquiring access to a mutex - you're given a "Guard" object which will release its respective lock once its dropped, and ModeConfigGuard is our version of this. ModeConfigGuards can either be "owned" or "borrowed". "Owned" means the rust code acquired the lock itself and thus - the lock must be dropped when the guard goes out of scope. "Borrowed" guards can only be created using unsafe functions, and are basically simply a promise that the mode config lock is held for the entire scope in which the ModeConfigGuard is alive. Borrowed guards are really only something that will be used on the FFI side of things for callbacks like drm_connector.get_modes() - where we know that the mode_config_lock was acquired prior to our get_modes() callback being invoked by DRM. ConnectorGuard is basically just a helpful extension of this that can be instantiated cheaply from a ModeConfigGuard, and it basically just provides access to methods for individual connectors that can only happen under the mode_config lock. You can see an example of this in rust/kernel/drm/kms/connector.rs and drivers/gpu/drm/rvkms/connector.rs. Keep in mind we currently don't have an implementation of an owned ModeConfigGuard, as no use for one has arisen in rvkms quite yet. Note: I did (and still am) considering using the kernel's already-made LockedBy<> type - however, this seems rather difficult to do considering that the BFL and connector live in different structs - and those structs are FFI structs. I shoud look a bit closer at some point soon though. As well, I expect the semantics around how this type works might change slightly - as I'd also like to see if we can represent the difference between owned and borrowed guards such that no checks need to be done at runtime. # Actually registering a modesetting device This part is still very much in flux I think, and it's worth explaining why. Currently from the DRM bindings we inherited from Asahi, the lifetime of a DRM device's registration is represented by drm::drv::Registration<T>. Unsurprisingly, the purpose of this is that once the Registration<T> object is dropped - the DRM device is unregistered. There's some complications around this though. The big one is that modesetting initialization is single threaded. This should be fine, but, currently (following how Asahi's driver does this) we ensure automatic reg/unreg by sticking the Registration<T> object in our registration device data (see `device::Data<T, U, V>` for an explanation of what this means). And that would also be fine, except for the fact that said device data has to be thread safe. This means it needs Send/Sync, which also means we basically have to treat the registration as multi-threaded despite the fact it really should never actually be happening in multiple threads. We also have a RegistrationInfo struct i had to come up with, which provides access to an atomic boolean that tracks whether or not we've actually registered the device. This part was only needed because while DRM does have a variable for tracking this in drm_device already, the Registration<T> object is created before registration actually happens - so it needs a thread-safe way of knowing whether or not it needs to unregister the device upon being dropped. The other thing I haven't been able to figure out: a way of safely gating global modesetting device functions so that they only can be used on DRM devices that support KMS (an example of one such function - drm_mode_config_reset()). Since the callbacks for modesetting devices and the rest of DRM live in the same struct, the same struct is used for both cases - even though it's entirely possible to have a drm_device without KMS support and thus without an initialized mode_config struct. This would be very nice to figure out, because I assume there's likely to be UB if a non-KMS device attempts to do KMS-like operations on itself. Currently, a modesetting device indicates it has KMS in my branch by doing two things: * Setting FEAT_MODESET and FEAT_ATOMIC in drm::drv::Driver::FEATURES * Passing a ModeConfigInfo struct to drm::drv::Registration::register(), containing various misc. information drivers usually populate in mode_config Figuring out how to gate these to only KMS-supporting devices would likely mean moving the global modesetting callbacks we need to support into a different trait that's only implemented by KMS drivers - but I'm not quite sure how to do that cleanly yet. # Other issues/hacks * Currently, a DRM driver's vtable and file operations table are not static. I totally think we can (and should) make this static by making drm::gem::create_fops() a const fn, and also turning DriverOps's constructors into const fns. The current blocker for this is that Default::default() is not const, along with mem::zeroed() - giving us no way of creating a zero-initialized struct at compile-time. Coincidentally, mem::zeroed() actually becomes const in rust 1.75 - so once the kernel updates its rust version we should be able to fix this. * There is a leak somewhere? Unloading rvkms currently leaves behind a few DRI directories, I'm not totally sure why yet - but I think this may be an issue with the DRM bindings themselves. * bindgen doesn't understand fourcc, and probably a number of other similar files. So we're going to need some nasty hacks to expose these. * I'm sure there's bits of code that need cleaning up, but I figured it was more important to start getting feedback on all of this first :). Lyude Paul (4): WIP: rust: Add basic KMS bindings WIP: drm: Introduce rvkms rust/drm/kms: Extract PlaneState<T> into IntoPlaneState WIP: rust/drm/kms: Add ShadowPlaneState<T> drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/rvkms/Kconfig | 3 + drivers/gpu/drm/rvkms/Makefile | 1 + drivers/gpu/drm/rvkms/connector.rs | 55 +++ drivers/gpu/drm/rvkms/crtc.rs | 40 +++ drivers/gpu/drm/rvkms/encoder.rs | 26 ++ drivers/gpu/drm/rvkms/file.rs | 22 ++ drivers/gpu/drm/rvkms/gem.rs | 32 ++ drivers/gpu/drm/rvkms/output.rs | 72 ++++ drivers/gpu/drm/rvkms/plane.rs | 42 +++ drivers/gpu/drm/rvkms/rvkms.rs | 146 ++++++++ rust/bindings/bindings_helper.h | 6 + rust/helpers.c | 17 + rust/kernel/drm/device.rs | 2 + rust/kernel/drm/drv.rs | 115 ++++++- rust/kernel/drm/kms.rs | 147 +++++++++ rust/kernel/drm/kms/connector.rs | 404 +++++++++++++++++++++++ rust/kernel/drm/kms/crtc.rs | 300 +++++++++++++++++ rust/kernel/drm/kms/encoder.rs | 175 ++++++++++ rust/kernel/drm/kms/gem_atomic_helper.rs | 48 +++ rust/kernel/drm/kms/plane.rs | 339 +++++++++++++++++++ rust/kernel/drm/mod.rs | 1 + 23 files changed, 1980 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/rvkms/Kconfig create mode 100644 drivers/gpu/drm/rvkms/Makefile create mode 100644 drivers/gpu/drm/rvkms/connector.rs create mode 100644 drivers/gpu/drm/rvkms/crtc.rs create mode 100644 drivers/gpu/drm/rvkms/encoder.rs create mode 100644 drivers/gpu/drm/rvkms/file.rs create mode 100644 drivers/gpu/drm/rvkms/gem.rs create mode 100644 drivers/gpu/drm/rvkms/output.rs create mode 100644 drivers/gpu/drm/rvkms/plane.rs create mode 100644 drivers/gpu/drm/rvkms/rvkms.rs create mode 100644 rust/kernel/drm/kms.rs create mode 100644 rust/kernel/drm/kms/connector.rs create mode 100644 rust/kernel/drm/kms/crtc.rs create mode 100644 rust/kernel/drm/kms/encoder.rs create mode 100644 rust/kernel/drm/kms/gem_atomic_helper.rs create mode 100644 rust/kernel/drm/kms/plane.rs