From nobody Thu Apr 2 17:32:51 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E6CD3921C9 for ; Fri, 20 Mar 2026 23:37:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049834; cv=none; b=YFEmAhuFy6ldix0hRfds7aZ2Ms0A0Ry7XpkwrsjGVoitSXsR2pCshplDKJeOhHWnpkFUCuK8UOsQu0LhWchRHdsrLoHnNCa6sBk3uFtfm51z/5bT/oP2GyzYwc5pqSjSjFzxoxrppUXOYosyLI7eVuqtj0PfWylU9jvzLxWHLtw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049834; c=relaxed/simple; bh=jMl8Xxr2nmNohC+5tBcpcjRj8bRkisB3AHxNwYGtSp4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gRnNcEHWAiT2wGj97r5lmAXWpzaWOW1QkZ/SU57+CzzHNlNweYcM38P2iVEuQv3y6dKlzTCp0kAMb36m9UnQnRbh5Wt6pGkKkRu4eifPtuGTVJ0KOBVkeqjM3LPxrQmj8+mmO4tT3UI7FACcNrLybdMRqL96a+cgn2A043+Gnjw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=bWGpNrqc; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="bWGpNrqc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774049831; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sxiOvgN5DZSPamM+FiFpphp0MrOzJ23HjWyPejweAYg=; b=bWGpNrqcrafkXis7uINc54HQZEdFxfEROyH+Hcw08EwyORogHy91xfdZ4pi7sftqR5VUW5 MjW4J8uideS/ZfaDZLkLyvQw/AtGeAZg7/0Lb53xYlXaR9JIs1S2sgYENibjkfdDcK6VZ/ Q8MvfRa7P5cR09q9cdy2vBn3E2re7LU= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-106-YI8hEWmMNjeTCdhfZeRguw-1; Fri, 20 Mar 2026 19:37:08 -0400 X-MC-Unique: YI8hEWmMNjeTCdhfZeRguw-1 X-Mimecast-MFC-AGG-ID: YI8hEWmMNjeTCdhfZeRguw_1774049826 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B8492195608C; Fri, 20 Mar 2026 23:37:05 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.80.37]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4AC1A180075C; Fri, 20 Mar 2026 23:37:03 +0000 (UTC) From: Lyude Paul To: linux-kernel@vger.kernel.org, Danilo Krummrich , rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org, nouveau@lists.freedesktop.org, "Gary Guo" , "Miguel Ojeda" , "Alice Ryhl" , "Simona Vetter" , "Shankari Anand" , "Maxime Ripard" , "David Airlie" , "Benno Lossin" , "Asahi Lina" , "Daniel Almeida" , "Lyude Paul" Subject: [PATCH v6 1/5] rust/drm: Fix potential drop of uninitialized driver data Date: Fri, 20 Mar 2026 19:34:26 -0400 Message-ID: <20260320233645.950190-2-lyude@redhat.com> In-Reply-To: <20260320233645.950190-1-lyude@redhat.com> References: <20260320233645.950190-1-lyude@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" It was pointed out during patch review that if we fail to initialize the driver's private data in drm::device::Device::new(), we end up calling drm_dev_put(). This would call down to release(), which calls core::ptr::drop_in_place() on the device, which would result in releasing currently uninitialized private driver data. So, fix this by just keeping track of when the private driver data is initialized or not and sticking it in a MaybeUninit. Signed-off-by: Lyude Paul Fixes: 1e4b8896c0f3 ("rust: drm: add device abstraction") Cc: # v6.16+ --- rust/kernel/drm/device.rs | 53 +++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 629ef0bd1188e..38ae8de0af5d6 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -22,12 +22,14 @@ }; use core::{ alloc::Layout, - mem, - ops::Deref, - ptr::{ + cell::UnsafeCell, + mem::{ self, - NonNull, // + MaybeUninit, // }, + ops::Deref, + ptr::NonNull, + sync::atomic::*, }; =20 #[cfg(CONFIG_DRM_LEGACY)] @@ -71,7 +73,14 @@ macro_rules! drm_legacy_fields { #[repr(C)] pub struct Device { dev: Opaque, - data: T::Data, + + /// Keeps track of whether we've initialized the device data yet. + pub(super) data_is_init: AtomicBool, + + /// The Driver's private data. + /// + /// This must only be written to from [`Device::new`]. + pub(super) data: UnsafeCell>, } =20 impl Device { @@ -128,8 +137,13 @@ pub fn new(dev: &device::Device, data: impl PinInit) -> Result from UnsafeCell> // SAFETY: `raw_drm` is a valid pointer to `Self`. - let raw_data =3D unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).da= ta) }; + let raw_data =3D unsafe { (*(raw_drm.as_ptr())).data.get() }; + + // Extract *mut T::Data from *mut MaybeUninit + // SAFETY: `raw_data` is derived from `raw_drm` which is a valid p= ointer to `Self`. + let raw_data =3D unsafe { (*raw_data).as_mut_ptr() }; =20 // SAFETY: // - `raw_data` is a valid pointer to uninitialized memory. @@ -144,6 +158,14 @@ pub fn new(dev: &device::Device, data: impl PinInit) -> Result Deref for Device { type Target =3D T::Data; =20 fn deref(&self) -> &Self::Target { - &self.data + // SAFETY: `data` is only written to once in `Device::new()`, so t= his read will never race. + unsafe { (&*self.data.get()).assume_init_ref() } } } =20 --=20 2.53.0 From nobody Thu Apr 2 17:32:51 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4665436C9FC for ; Fri, 20 Mar 2026 23:37:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049844; cv=none; b=h5hLbQU9kP0smXJuaAZTOzOHo9MivQfnGeF9HSMu6AwiyiiS5vMAqpq3JsOo/SBZILTNcL5VtLoiAQAQ516j1JtftJSyem5hf9RwcCBHuAmXyE5VSbkHkghM9sy43JDwFXt/6K444TR/eBJWmJrLf9HPVCCrkmxcVguYri2ad5o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049844; c=relaxed/simple; bh=Yl5cr7hTW7rxeA4Dv674mW8oXuo+jYwptHxO2dcj3Pk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=A3WGRtKe7z4+q4DYXb4aZiYl+/LsqnaYNkWFcvKvxEUiSA5JhZTN0Jh6xVM3N7ThmQJvGp5PHQZivqzEOC+AgnNLzBknrcTvLL9SYjR1fqBbMST4rmcPuGa6d9fVmNz6r8ZY/u1kI/wO2TgePd6g56NS5IaYjLYhO6TKV3xkI8E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=AbciPP8z; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="AbciPP8z" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774049841; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5nsTo0iGp0cMgU7WrN9x3Sl9aCgAeNVK+9RW1jx6x+8=; b=AbciPP8zHt2PqXLGGhb4pDgYKYQKZD2SEwW9I8oaTwLE5W9z49uDW+7ZDt10Owsj+pzXDL 4HNT+2nowcNAAWNv/jzEEhhbH74EYQn1GKPBOEBPZn6sLDc8l2AkZ9S40NJlzRxVCnDXmp Z08qIYAvRDMyMv+hDIvr9WVs8MtThyc= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-588-Z-0DXhXZP-e3UJYVcmxfLg-1; Fri, 20 Mar 2026 19:37:16 -0400 X-MC-Unique: Z-0DXhXZP-e3UJYVcmxfLg-1 X-Mimecast-MFC-AGG-ID: Z-0DXhXZP-e3UJYVcmxfLg_1774049834 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 93A191944EB0; Fri, 20 Mar 2026 23:37:13 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.80.37]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 317EF180075C; Fri, 20 Mar 2026 23:37:11 +0000 (UTC) From: Lyude Paul To: linux-kernel@vger.kernel.org, Danilo Krummrich , rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Daniel Almeida , nouveau@lists.freedesktop.org, "Gary Guo" , "Miguel Ojeda" , "Simona Vetter" , "Alice Ryhl" , "Shankari Anand" , "Maxime Ripard" , "David Airlie" , "Benno Lossin" , "Asahi Lina" , "Lyude Paul" Subject: [PATCH v6 2/5] rust/drm: Introduce DeviceContext Date: Fri, 20 Mar 2026 19:34:27 -0400 Message-ID: <20260320233645.950190-3-lyude@redhat.com> In-Reply-To: <20260320233645.950190-1-lyude@redhat.com> References: <20260320233645.950190-1-lyude@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 One of the tricky things about DRM bindings in Rust is the fact that initialization of a DRM device is a multi-step process. It's quite normal for a device driver to start making use of its DRM device for tasks like creating GEM objects before userspace registration happens. This is an issue in rust though, since prior to userspace registration the device is only partly initialized. This means there's a plethora of DRM device operations we can't yet expose without opening up the door to UB if the DRM device in question isn't yet registered. Additionally, this isn't something we can reliably check at runtime. And even if we could, performing an operation which requires the device be registered when the device isn't actually registered is a programmer bug, meaning there's no real way to gracefully handle such a mistake at runtime. And even if that wasn't the case, it would be horrendously annoying and noisy to have to check if a device is registered constantly throughout a driver. In order to solve this, we first take inspiration from `kernel::device::DeviceContext` and introduce `kernel::drm::DeviceContext`. This provides us with a ZST type that we can generalize over to represent contexts where a device is known to have been registered with userspace at some point in time (`Registered`), along with contexts where we can't make such a guarantee (`Uninit`). It's important to note we intentionally do not provide a `DeviceContext` which represents an unregistered device. This is because there's no reasonable way to guarantee that a device with long-living references to itself will not be registered eventually with userspace. Instead, we provide a new-type for this: `UnregisteredDevice` which can provide a guarantee that the `Device` has never been registered with userspace. To ensure this, we modify `Registration` so that creating a new `Registration` requires passing ownership of an `UnregisteredDevice`. Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida --- V2: * Make sure that `UnregisteredDevice` is not thread-safe (since DRM device initialization is also not thread-safe) * Rename from AnyCtx to Uninit, I think this name actually makes a bit more sense. * Change assume_registered() to assume_ctx() Since it looks like in some situations, we'll want to update the DeviceContext of a object to the latest DeviceContext we know the Device to be in. * Rename Init to Uninit When we eventually add KMS support, we're going to have 3 different DeviceContexts - Uninit, Init, Registered. Additionally, aside from not being registered there are a number of portions of the rest of the Device which also aren't usable before at least the Init context - so the naming of Uninit makes this a little clearer. * s/DeviceContext/DeviceContext/ For consistency with the rest of the kernel * Drop as_ref::>() for now since I don't actually think we need this quite yet V3: * Get rid of drm_dev_ctx!, as we don't actually need to implement Send or Sync ourselves * Remove mention of C function in drm::device::Registration rustdoc * Add more documentation to the DeviceContext trait, go into detail about the various setup phases and such. * Add missing period to comment in `UnregisteredDevice::new()`. V4: * Address some comments from Danilo I missed last round: * Remove leftover rebase detritus from new_foreign_owned() (the seemingly useless cast) * Remove no-op mention in Registered device context V5: * Fix incorrect size on Kmalloc (Deborah) drivers/gpu/drm/nova/driver.rs | 8 +- drivers/gpu/drm/tyr/driver.rs | 10 +- rust/kernel/drm/device.rs | 191 +++++++++++++++++++++++++++------ rust/kernel/drm/driver.rs | 35 ++++-- rust/kernel/drm/mod.rs | 4 + 5 files changed, 197 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index b1af0a099551d..99d6841b69cbc 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -21,7 +21,7 @@ pub(crate) struct NovaDriver { } =20 /// Convienence type alias for the DRM device type for this driver -pub(crate) type NovaDevice =3D drm::Device; +pub(crate) type NovaDevice =3D drm::Device; =20 #[pin_data] pub(crate) struct NovaData { @@ -56,10 +56,10 @@ impl auxiliary::Driver for NovaDriver { fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl= PinInit { let data =3D try_pin_init!(NovaData { adev: adev.into() }); =20 - let drm =3D drm::Device::::new(adev.as_ref(), data)?; - drm::Registration::new_foreign_owned(&drm, adev.as_ref(), 0)?; + let drm =3D drm::UnregisteredDevice::::new(adev.as_ref(), da= ta)?; + let drm =3D drm::Registration::new_foreign_owned(drm, adev.as_ref(= ), 0)?; =20 - Ok(Self { drm }) + Ok(Self { drm: drm.into() }) } } =20 diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 6114346415805..6238f6e2b3bd2 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -42,7 +42,7 @@ pub(crate) struct TyrDrmDriver; =20 /// Convenience type alias for the DRM device type for this driver. -pub(crate) type TyrDrmDevice =3D drm::Device; +pub(crate) type TyrDrmDevice =3D drm::Device; =20 #[pin_data(PinnedDrop)] pub(crate) struct TyrPlatformDriverData { @@ -145,10 +145,12 @@ fn probe( gpu_info, }); =20 - let ddev: ARef =3D drm::Device::new(pdev.as_ref(), d= ata)?; - drm::driver::Registration::new_foreign_owned(&ddev, pdev.as_ref(),= 0)?; + let tdev =3D drm::UnregisteredDevice::::new(pdev.as_= ref(), data)?; + let tdev =3D drm::driver::Registration::new_foreign_owned(tdev, pd= ev.as_ref(), 0)?; =20 - let driver =3D TyrPlatformDriverData { _device: ddev }; + let driver =3D TyrPlatformDriverData { + _device: tdev.into(), + }; =20 // We need this to be dev_info!() because dev_dbg!() does not work= at // all in Rust for now, and we need to see whether probe succeeded. diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 38ae8de0af5d6..89788af2e6537 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -10,7 +10,8 @@ device, drm::{ self, - driver::AllocImpl, // + driver::AllocImpl, + private::Sealed, // }, error::from_err_ptr, prelude::*, @@ -18,11 +19,15 @@ ARef, AlwaysRefCounted, // }, - types::Opaque, // + types::{ + NotThreadSafe, + Opaque, // + }, // }; use core::{ alloc::Layout, cell::UnsafeCell, + marker::PhantomData, mem::{ self, MaybeUninit, // @@ -63,33 +68,98 @@ macro_rules! drm_legacy_fields { } } =20 -/// A typed DRM device with a specific `drm::Driver` implementation. +/// A trait implemented by all possible contexts a [`Device`] can be used = in. /// -/// The device is always reference-counted. +/// Setting up a new [`Device`] is a multi-stage process. Each step of the= process that a user +/// interacts with in Rust has a respective [`DeviceContext`] typestate. F= or example, +/// `Device` would be a [`Device`] that reached the [`Regis= tered`] [`DeviceContext`]. +/// +/// Each stage of this process is described below: +/// +/// ```text +/// 1 2 3 +/// +--------------+ +------------------+ +-----------------------+ +/// |Device created| =E2=86=92 |Device initialized| =E2=86=92 |Registered = w/ userspace| +/// +--------------+ +------------------+ +-----------------------+ +/// (Uninit) (Registered) +/// ``` +/// +/// 1. The [`Device`] is in the [`Uninit`] context and is not guaranteed t= o be initialized or +/// registered with userspace. Only a limited subset of DRM core functi= onality is available. +/// 2. The [`Device`] is guaranteed to be fully initialized, but is not gu= aranteed to be registered +/// with userspace. All DRM core functionality which doesn't interact w= ith userspace is +/// available. We currently don't have a context for representing this. +/// 3. The [`Device`] is guaranteed to be fully initialized, and is guaran= teed to have been +/// registered with userspace at some point - thus putting it in the [`= Registered`] context. +/// +/// An important caveat of [`DeviceContext`] which must be kept in mind: w= hen used as a typestate +/// for a reference type, it can only guarantee that a [`Device`] reached = a particular stage in the +/// initialization process _at the time the reference was taken_. No guara= ntee is made in regards to +/// what stage of the process the [`Device`] is currently in. This means f= or instance that a +/// `&Device` may actually be registered with userspace, it jus= t wasn't known to be +/// registered at the time the reference was taken. +pub trait DeviceContext: Sealed + Send + Sync {} + +/// The [`DeviceContext`] of a [`Device`] that was registered with userspa= ce at some point. +/// +/// This represents a [`Device`] which is guaranteed to have been register= ed with userspace at +/// some point in time. Such a DRM device is guaranteed to have been fully= -initialized. +/// +/// Note: A device in this context is not guaranteed to remain registered = with userspace for its +/// entire lifetime, as this is impossible to guarantee at compile-time. /// /// # Invariants /// -/// `self.dev` is a valid instance of a `struct device`. -#[repr(C)] -pub struct Device { - dev: Opaque, +/// A [`Device`] in this [`DeviceContext`] is guaranteed to have been regi= stered with userspace +/// at some point in time. +pub struct Registered; =20 - /// Keeps track of whether we've initialized the device data yet. - pub(super) data_is_init: AtomicBool, +impl Sealed for Registered {} +impl DeviceContext for Registered {} =20 - /// The Driver's private data. - /// - /// This must only be written to from [`Device::new`]. - pub(super) data: UnsafeCell>, +/// The [`DeviceContext`] of a [`Device`] that may be unregistered and par= tly uninitialized. +/// +/// A [`Device`] in this context is only guaranteed to be partly initializ= ed, and may or may not +/// be registered with userspace. Thus operations which depend on the [`De= vice`] being fully +/// initialized, or which depend on the [`Device`] being registered with u= serspace are not +/// available through this [`DeviceContext`]. +/// +/// A [`Device`] in this context can be used to create a +/// [`Registration`](drm::driver::Registration). +pub struct Uninit; + +impl Sealed for Uninit {} +impl DeviceContext for Uninit {} + +/// A [`Device`] which is known at compile-time to be unregistered with us= erspace. +/// +/// This type allows performing operations which are only safe to do befor= e userspace registration, +/// and can be used to create a [`Registration`](drm::driver::Registration= ) once the driver is ready +/// to register the device with userspace. +/// +/// Since DRM device initialization must be single-threaded, this object i= s not thread-safe. +/// +/// # Invariants +/// +/// The device in `self.0` is guaranteed to be a newly created [`Device`] = that has not yet been +/// registered with userspace until this type is dropped. +pub struct UnregisteredDevice(ARef>, Not= ThreadSafe); + +impl Deref for UnregisteredDevice { + type Target =3D Device; + + fn deref(&self) -> &Self::Target { + &self.0 + } } =20 -impl Device { +impl UnregisteredDevice { const VTABLE: bindings::drm_driver =3D drm_legacy_fields! { load: None, open: Some(drm::File::::open_callback), postclose: Some(drm::File::::postclose_callback), unload: None, - release: Some(Self::release), + release: Some(Device::::release), master_set: None, master_drop: None, debugfs_init: None, @@ -117,21 +187,23 @@ impl Device { =20 const GEM_FOPS: bindings::file_operations =3D drm::gem::create_fops(); =20 - /// Create a new `drm::Device` for a `drm::Driver`. - pub fn new(dev: &device::Device, data: impl PinInit) -= > Result> { + /// Create a new `UnregisteredDevice` for a `drm::Driver`. + /// + /// This can be used to create a [`Registration`](kernel::drm::Registr= ation). + pub fn new(dev: &device::Device, data: impl PinInit) -= > Result { // `__drm_dev_alloc` uses `kmalloc()` to allocate memory, hence en= sure a `kmalloc()` // compatible `Layout`. - let layout =3D Kmalloc::aligned_layout(Layout::new::()); + let layout =3D Kmalloc::aligned_layout(Layout::new::>()); =20 // 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 =3D unsafe { + let raw_drm: *mut Device =3D unsafe { bindings::__drm_dev_alloc( dev.as_raw(), &Self::VTABLE, layout.size(), - mem::offset_of!(Self, dev), + mem::offset_of!(Device, dev), ) } .cast(); @@ -151,7 +223,7 @@ pub fn new(dev: &device::Device, data: impl PinInit) -> Result) -> Result { + dev: Opaque, + + /// Keeps track of whether we've initialized the device data yet. + pub(super) data_is_init: AtomicBool, =20 + /// The Driver's private data. + /// + /// This must only be written to from [`Device::new`]. + pub(super) data: UnsafeCell>, + + _ctx: PhantomData, +} + +impl Device { pub(crate) fn as_raw(&self) -> *mut bindings::drm_device { self.dev.get() } @@ -196,13 +306,13 @@ unsafe fn into_drm_device(ptr: NonNull) -> *mut= bindings::drm_device { /// /// # Safety /// - /// Callers must ensure that `ptr` is valid, non-null, and has a non-z= ero reference count, - /// i.e. it must be ensured that the reference count of the C `struct = drm_device` `ptr` points - /// to can't drop to zero, for the duration of this function call and = the entire duration when - /// the returned reference exists. - /// - /// Additionally, callers must ensure that the `struct device`, `ptr` = is pointing to, is - /// embedded in `Self`. + /// * Callers must ensure that `ptr` is valid, non-null, and has a non= -zero reference count, + /// i.e. it must be ensured that the reference count of the C `struc= t drm_device` `ptr` points + /// to can't drop to zero, for the duration of this function call an= d the entire duration when + /// the returned reference exists. + /// * Additionally, callers must ensure that the `struct device`, `ptr= ` is pointing to, is + /// embedded in `Self`. + /// * Callers promise that any type invariants of `C` will be upheld. #[doc(hidden)] pub unsafe fn from_raw<'a>(ptr: *const bindings::drm_device) -> &'a Se= lf { // SAFETY: By the safety requirements of this function `ptr` is a = valid pointer to a @@ -238,9 +348,20 @@ extern "C" fn release(ptr: *mut bindings::drm_device) { // - `this` is valid for dropping. unsafe { core::ptr::drop_in_place(this) }; } + + /// Change the [`DeviceContext`] for a [`Device`]. + /// + /// # Safety + /// + /// The caller promises that `self` fulfills all of the guarantees pro= vided by the given + /// [`DeviceContext`]. + pub(crate) unsafe fn assume_ctx(&self) -> &Devi= ce { + // SAFETY: The data layout is identical via our type invariants. + unsafe { mem::transmute(self) } + } } =20 -impl Deref for Device { +impl Deref for Device { type Target =3D T::Data; =20 fn deref(&self) -> &Self::Target { @@ -251,7 +372,7 @@ fn deref(&self) -> &Self::Target { =20 // SAFETY: DRM device objects are always reference counted and the get/put= functions // satisfy the requirements. -unsafe impl AlwaysRefCounted for Device { +unsafe impl AlwaysRefCounted for Device<= T, C> { fn inc_ref(&self) { // SAFETY: The existence of a shared reference guarantees that the= refcount is non-zero. unsafe { bindings::drm_dev_get(self.as_raw()) }; @@ -266,7 +387,7 @@ unsafe fn dec_ref(obj: NonNull) { } } =20 -impl AsRef for Device { +impl AsRef for Device { fn as_ref(&self) -> &device::Device { // SAFETY: `bindings::drm_device::dev` is valid as long as the DRM= device itself is valid, // which is guaranteed by the type invariant. @@ -275,8 +396,8 @@ fn as_ref(&self) -> &device::Device { } =20 // SAFETY: A `drm::Device` can be released from any thread. -unsafe impl Send for Device {} +unsafe impl Send for Device {} =20 // SAFETY: A `drm::Device` can be shared among threads because all immutab= le methods are protected // by the synchronization in `struct drm_device`. -unsafe impl Sync for Device {} +unsafe impl Sync for Device {} diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index 5233bdebc9fcd..55b01ee088548 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -13,6 +13,10 @@ prelude::*, sync::aref::ARef, // }; +use core::{ + mem, + ptr::NonNull, // +}; =20 /// Driver use the GEM memory manager. This should be set for all modern d= rivers. pub(crate) const FEAT_GEM: u32 =3D bindings::drm_driver_feature_DRIVER_GEM; @@ -123,21 +127,31 @@ pub trait Driver { pub struct Registration(ARef>); =20 impl Registration { - fn new(drm: &drm::Device, flags: usize) -> Result { + fn new(drm: drm::UnregisteredDevice, flags: usize) -> Result { // SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Devi= ce`. to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags)= })?; =20 - Ok(Self(drm.into())) + // SAFETY: We just called `drm_dev_register` above + let new =3D NonNull::from(unsafe { drm.assume_ctx() }); + + // Leak the ARef from UnregisteredDevice in preparation for transf= erring its ownership. + mem::forget(drm); + + // SAFETY: `drm`'s `Drop` constructor was never called, ensuring t= hat there remains at least + // one reference to the device - which we take ownership over here. + let new =3D unsafe { ARef::from_raw(new) }; + + Ok(Self(new)) } =20 - /// Registers a new [`Device`](drm::Device) with userspace. + /// Registers a new [`UnregisteredDevice`](drm::UnregisteredDevice) wi= th userspace. /// /// Ownership of the [`Registration`] object is passed to [`devres::re= gister`]. - pub fn new_foreign_owned( - drm: &drm::Device, - dev: &device::Device, + pub fn new_foreign_owned<'a>( + drm: drm::UnregisteredDevice, + dev: &'a device::Device, flags: usize, - ) -> Result + ) -> Result<&'a drm::Device> where T: 'static, { @@ -146,8 +160,13 @@ pub fn new_foreign_owned( } =20 let reg =3D Registration::::new(drm, flags)?; + let drm =3D NonNull::from(reg.device()); + + devres::register(dev, reg, GFP_KERNEL)?; =20 - devres::register(dev, reg, GFP_KERNEL) + // SAFETY: Since `reg` was passed to devres::register(), the devic= e now owns the lifetime + // of the DRM registration - ensuring that this references lives f= or at least as long as 'a. + Ok(unsafe { drm.as_ref() }) } =20 /// Returns a reference to the `Device` instance for this registration. diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs index 1b82b6945edf2..64a43cb0fe57c 100644 --- a/rust/kernel/drm/mod.rs +++ b/rust/kernel/drm/mod.rs @@ -9,6 +9,10 @@ pub mod ioctl; =20 pub use self::device::Device; +pub use self::device::DeviceContext; +pub use self::device::Registered; +pub use self::device::Uninit; +pub use self::device::UnregisteredDevice; pub use self::driver::Driver; pub use self::driver::DriverInfo; pub use self::driver::Registration; --=20 2.53.0 From nobody Thu Apr 2 17:32:51 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 81AC336682A for ; Fri, 20 Mar 2026 23:37:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049851; cv=none; b=RYNl0RgVNOXVB4k7/1jSmFc65SRwT5KdlCOKfsWdu7oF8ZgjO4kE6h/rm3Fs0tc0pW4dlhITnCx/+IwpfJRg96/+6PTOeGnBTEi60CuC01JSh0/Ab2Ur0zPbE+fXgxu2sOrIOvMudrDoIUaabSIwNYmsX3erp1h96eCjDJ71jz0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049851; c=relaxed/simple; bh=2WvCPzAGq9WA6Hcc8/hhqrH/1BFO7dGZQc66CFPIXgA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TTwRixFgn9dsnNktUZ6nL2oOjlJVDAqumfZreW4+U6QWrDgzg8beaksOg4gLdiPkB8RmuJAWsv3H1zl8gAH/ry3g/ujDW2C4vcvIuQqaAEVKR9DenU/c2hK1Sa1W3iqhTJVSsE9wHpP0x0ZoFy5AgAMBr4Xg+Y+OfpAalElDXRk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=arMrVqoE; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="arMrVqoE" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774049848; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=9ryjuW225aIyPKriHsFTqwz9JH8KJWoGfZOzCp5X0/o=; b=arMrVqoEfn6TfN+JLcxQuo78fG7UsIjPhx2N1SgJkZPavYZe8bofATQnGCwhsb576tW4zG yFccFyyELoeY4HN2B+LOOYNLFujte1aLzl4lA0gOvaw98f0dMDi2QprZMZGKyA0SH3jQgW D6t7th2u3uxwehd3JSmBCwRrxSkrTKw= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-118-81S9b9teMgmwjUvT-fVnCg-1; Fri, 20 Mar 2026 19:37:23 -0400 X-MC-Unique: 81S9b9teMgmwjUvT-fVnCg-1 X-Mimecast-MFC-AGG-ID: 81S9b9teMgmwjUvT-fVnCg_1774049841 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 254FC1800345; Fri, 20 Mar 2026 23:37:21 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.80.37]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0405E180075C; Fri, 20 Mar 2026 23:37:18 +0000 (UTC) From: Lyude Paul To: linux-kernel@vger.kernel.org, Danilo Krummrich , rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Daniel Almeida , nouveau@lists.freedesktop.org, "Gary Guo" , "Miguel Ojeda" , "Alice Ryhl" , "Simona Vetter" , "Shankari Anand" , "Maxime Ripard" , "David Airlie" , "Benno Lossin" , "Asahi Lina" , "Lyude Paul" Subject: [PATCH v6 3/5] rust/drm: Don't setup private driver data until registration Date: Fri, 20 Mar 2026 19:34:28 -0400 Message-ID: <20260320233645.950190-4-lyude@redhat.com> In-Reply-To: <20260320233645.950190-1-lyude@redhat.com> References: <20260320233645.950190-1-lyude@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Now that we have a DeviceContext that we can use to represent whether a Device is known to have been registered, we can make it so that drivers can create their Devices but wait until the registration phase to assign their private data to the Device. This is desirable as some drivers need to make use of the DRM device early on before finalizing their private driver data. As such, this change makes it so that the driver's private data can currently only be accessed through Device types and not Device. Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida --- V4: * Remove accidental double-aliasing &mut in Device::release() V5: * Change new members of Device to pub(super) V6: * Split out data_is_init addition into a separate patch, as it turns out this fixes a previously present issue in the rust DRM bindings. * s/pub(crate)/pub(super)/ for one instance I missed in V5 drivers/gpu/drm/nova/driver.rs | 4 ++-- drivers/gpu/drm/tyr/driver.rs | 4 ++-- rust/kernel/drm/device.rs | 42 +++++++--------------------------- rust/kernel/drm/driver.rs | 19 ++++++++++++--- 4 files changed, 28 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 99d6841b69cbc..8cea5f68c3b04 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -56,8 +56,8 @@ impl auxiliary::Driver for NovaDriver { fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl= PinInit { let data =3D try_pin_init!(NovaData { adev: adev.into() }); =20 - let drm =3D drm::UnregisteredDevice::::new(adev.as_ref(), da= ta)?; - let drm =3D drm::Registration::new_foreign_owned(drm, adev.as_ref(= ), 0)?; + let drm =3D drm::UnregisteredDevice::::new(adev.as_ref())?; + let drm =3D drm::Registration::new_foreign_owned(drm, adev.as_ref(= ), data, 0)?; =20 Ok(Self { drm: drm.into() }) } diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 6238f6e2b3bd2..76c5aed1171b1 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -145,8 +145,8 @@ fn probe( gpu_info, }); =20 - let tdev =3D drm::UnregisteredDevice::::new(pdev.as_= ref(), data)?; - let tdev =3D drm::driver::Registration::new_foreign_owned(tdev, pd= ev.as_ref(), 0)?; + let tdev =3D drm::UnregisteredDevice::::new(pdev.as_= ref())?; + let tdev =3D drm::driver::Registration::new_foreign_owned(tdev, pd= ev.as_ref(), data, 0)?; =20 let driver =3D TyrPlatformDriverData { _device: tdev.into(), diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 89788af2e6537..64b1089ba00d4 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -143,7 +143,7 @@ impl DeviceContext for Uninit {} /// /// The device in `self.0` is guaranteed to be a newly created [`Device`] = that has not yet been /// registered with userspace until this type is dropped. -pub struct UnregisteredDevice(ARef>, Not= ThreadSafe); +pub struct UnregisteredDevice(pub(super) ARef>, NotThreadSafe); =20 impl Deref for UnregisteredDevice { type Target =3D Device; @@ -190,7 +190,7 @@ impl UnregisteredDevice { /// Create a new `UnregisteredDevice` for a `drm::Driver`. /// /// This can be used to create a [`Registration`](kernel::drm::Registr= ation). - pub fn new(dev: &device::Device, data: impl PinInit) -= > Result { + pub fn new(dev: &device::Device) -> Result { // `__drm_dev_alloc` uses `kmalloc()` to allocate memory, hence en= sure a `kmalloc()` // compatible `Layout`. let layout =3D Kmalloc::aligned_layout(Layout::new::>()); @@ -209,35 +209,6 @@ pub fn new(dev: &device::Device, data: impl PinInit) -> Result from UnsafeCell> - // SAFETY: `raw_drm` is a valid pointer to `Self`. - let raw_data =3D unsafe { (*(raw_drm.as_ptr())).data.get() }; - - // Extract *mut T::Data from *mut MaybeUninit - // SAFETY: `raw_data` is derived from `raw_drm` which is a valid p= ointer to `Self`. - let raw_data =3D unsafe { (*raw_data).as_mut_ptr() }; - - // 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: `raw_drm` is a valid pointer to `Self`, given that = `__drm_dev_alloc` was - // successful. - let drm_dev =3D unsafe { Device::into_drm_device(raw_drm) }; - - // SAFETY: `__drm_dev_alloc()` was successful, hence `drm_dev`= must be valid and the - // refcount must be non-zero. - unsafe { bindings::drm_dev_put(drm_dev) }; - })?; - - // SAFETY: We just initialized raw_drm above using __drm_dev_alloc= (), ensuring it is safe to - // dereference - unsafe { - (*raw_drm.as_ptr()) - .data_is_init - .store(true, Ordering::Relaxed) - }; - // SAFETY: The reference count is one, and now we take ownership o= f that reference as a // `drm::Device`. // INVARIANT: We just created the device above, but have yet to ca= ll `drm_dev_register`. @@ -274,7 +245,7 @@ pub struct Device { =20 /// The Driver's private data. /// - /// This must only be written to from [`Device::new`]. + /// This must only be written to from [`drm::Registration::new`]. pub(super) data: UnsafeCell>, =20 _ctx: PhantomData, @@ -361,11 +332,14 @@ pub(crate) unsafe fn assume_ctx(&self) -> &Device Deref for Device { +impl Deref for Device { type Target =3D T::Data; =20 fn deref(&self) -> &Self::Target { - // SAFETY: `data` is only written to once in `Device::new()`, so t= his read will never race. + // SAFETY: + // - `data` is initialized before any `Device`s with the `Register= ed` context are available + // to the user. + // - `data` is only written to once in `Registration::new()`, so t= his read will never race. unsafe { (&*self.data.get()).assume_init_ref() } } } diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index 55b01ee088548..cfb8884ece700 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -15,7 +15,8 @@ }; use core::{ mem, - ptr::NonNull, // + ptr::NonNull, + sync::atomic::*, // }; =20 /// Driver use the GEM memory manager. This should be set for all modern d= rivers. @@ -127,7 +128,18 @@ pub trait Driver { pub struct Registration(ARef>); =20 impl Registration { - fn new(drm: drm::UnregisteredDevice, flags: usize) -> Result { + fn new( + drm: drm::UnregisteredDevice, + data: impl PinInit, + flags: usize, + ) -> Result { + // SAFETY: + // - `raw_data` is a valid pointer to uninitialized memory. + // - `raw_data` will not move until it is dropped. + unsafe { data.__pinned_init(drm.0.data.get().cast()) }?; + + drm.data_is_init.store(true, Ordering::Relaxed); + // SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Devi= ce`. to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags)= })?; =20 @@ -150,6 +162,7 @@ fn new(drm: drm::UnregisteredDevice, flags: usize) -= > Result { pub fn new_foreign_owned<'a>( drm: drm::UnregisteredDevice, dev: &'a device::Device, + data: impl PinInit, flags: usize, ) -> Result<&'a drm::Device> where @@ -159,7 +172,7 @@ pub fn new_foreign_owned<'a>( return Err(EINVAL); } =20 - let reg =3D Registration::::new(drm, flags)?; + let reg =3D Registration::::new(drm, data, flags)?; let drm =3D NonNull::from(reg.device()); =20 devres::register(dev, reg, GFP_KERNEL)?; --=20 2.53.0 From nobody Thu Apr 2 17:32:51 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D39EA3D7D99 for ; Fri, 20 Mar 2026 23:37:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049856; cv=none; b=QqpVcz09g5laIMD3QXZ5ApwMmQ0as/DCGFKqAdzXLevzWPSpQQcC3SD4j4+Hq/NFxJx8u7r+QLivPWhjTbz1BsR/k5SOMloT5XAkukQTE5WQBI0ctwGirLgutH9RRGeT2rUo28a05zo3UhmEw972aXAglJqVpmjkPPe9JMCBITM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049856; c=relaxed/simple; bh=95XhE2vRHmBj7zOhUcC79nWX/DO2ady9qpvytGf4Qi8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Zskbpk1S7BdLCUEEZodWzBCLktFMFV0kucWNgcWR0vHTDGeYB7QfT0580+4Fdx2qI/spwmYUa7TQcT7uy7IdKk+cS2Xyoo4gCa6hbgS7VmnVZZqr70jd8zMZCH9QRBY+Mbp/o9TkKJt9XrAn0+cCP8iQ6wHW2k0gjPoVjUdDPc8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=QRpqDAn/; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="QRpqDAn/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774049854; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QouNEtmlaggCaQo2P5IO63zf/LsG/YgCtk54rapeiQI=; b=QRpqDAn/Xy9Q/0XGzFizl3Fmj7RQj7eohlZkX5oQeIbQ1X89YbrHmVpvNGykUW2s52dy6z PecjLRkLLpF0oe2J3qM4Vj1rAzEouoNPSIDUPcwdtV7nJR16OCxDR77JJP0vDAIRRSlJVi nKxfVigZj1P3/Xdpsm4UHIBhPQcf+gQ= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-563-4f6-IlVUOOmiogtqN-RcXA-1; Fri, 20 Mar 2026 19:37:30 -0400 X-MC-Unique: 4f6-IlVUOOmiogtqN-RcXA-1 X-Mimecast-MFC-AGG-ID: 4f6-IlVUOOmiogtqN-RcXA_1774049848 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B695F19560A2; Fri, 20 Mar 2026 23:37:28 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.80.37]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 66852180075C; Fri, 20 Mar 2026 23:37:26 +0000 (UTC) From: Lyude Paul To: linux-kernel@vger.kernel.org, Danilo Krummrich , rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Daniel Almeida , nouveau@lists.freedesktop.org, "Gary Guo" , "Miguel Ojeda" , "Alice Ryhl" , "Simona Vetter" , "Shankari Anand" , "Maxime Ripard" , "David Airlie" , "Benno Lossin" , "Asahi Lina" , "Lyude Paul" Subject: [PATCH v6 4/5] rust/drm/gem: Add DriverAllocImpl type alias Date: Fri, 20 Mar 2026 19:34:29 -0400 Message-ID: <20260320233645.950190-5-lyude@redhat.com> In-Reply-To: <20260320233645.950190-1-lyude@redhat.com> References: <20260320233645.950190-1-lyude@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" This is just a type alias that resolves into the AllocImpl for a given T: drm::gem::DriverObject Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida --- rust/kernel/drm/gem/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index b4199945db378..ad74c5159f725 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -33,6 +33,11 @@ /// [`DriverFile`]: drm::file::DriverFile pub type DriverFile =3D drm::File<<::Driver as drm::= Driver>::File>; =20 +/// A type alias for retrieving the current [`AllocImpl`] for a given [`Dr= iverObject`]. +/// +/// [`Driver`]: drm::Driver +pub type DriverAllocImpl =3D <::Driver as drm::Drive= r>::Object; + /// GEM object functions, which must be implemented by drivers. pub trait DriverObject: Sync + Send + Sized { /// Parent `Driver` for this object. @@ -42,12 +47,12 @@ pub trait DriverObject: Sync + Send + Sized { fn new(dev: &drm::Device, size: usize) -> impl PinInit; =20 /// Open a new handle to an existing object, associated with a File. - fn open(_obj: &::Object, _file: &DriverFi= le) -> Result { + fn open(_obj: &DriverAllocImpl, _file: &DriverFile) -> Res= ult { Ok(()) } =20 /// Close a handle to an existing object, associated with a File. - fn close(_obj: &::Object, _file: &DriverF= ile) {} + fn close(_obj: &DriverAllocImpl, _file: &DriverFile) {} } =20 /// Trait that represents a GEM object subtype --=20 2.53.0 From nobody Thu Apr 2 17:32:51 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF98E38E5C1 for ; Fri, 20 Mar 2026 23:37:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049864; cv=none; b=l1yT3N5YrXT9QPy/yVOHyqe+kaigVp5cbadn7gVeG32zMpaZO7GXadZzW9mTDWUiNe0xwxoGKPJVHa7sxLUP6eF6UaAPzx+17LFhtnJ92fiGRsb8ccYCygeIbowNa4GQpCYSUtPX+b4DIvGi8ruDRwfHPy740FqFFVv0yMkWQOw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774049864; c=relaxed/simple; bh=69mYU2c4z3R5nunTOCeCQl0BGvwlVBQyT3TDLzmBQc8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=neOCXNzKABcwl2ESMvDuXQQB2+5eLu7r8MHIbib4ucK1FYPrHMSd2EG9U1gTICt6JNTvUbEtvHlhjug0Kf1WiZqnKGUWuXURDrbVICK83AdzYpf81znCAgmjRQLS4EDvYtFJ6PRS3uBiWE/YevRzesY6bxYGag473ep8FfZxYRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=ZGEPWzRr; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="ZGEPWzRr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774049862; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wTujU8KlZfO8hSxf0gPug84kE4qsLXMCF+IRUSiCktA=; b=ZGEPWzRrn4A9PFOZAqd1mIBCHuT0SjEZqht+DrA0kRzc6/dhZNseSJ7h2PvutwI7xTbAwg lZpuGyj0H6dE9SPmmV3U2om9rkXTQzLBMGgMHxWc3VJYB07eC7o+1QiEIkldo7tmN37q/R NsVt7mCScp+J30mSKw2Pis1lEC8Xk80= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-304-J_YPp4UUN7ib7HmSSONqlQ-1; Fri, 20 Mar 2026 19:37:38 -0400 X-MC-Unique: J_YPp4UUN7ib7HmSSONqlQ-1 X-Mimecast-MFC-AGG-ID: J_YPp4UUN7ib7HmSSONqlQ_1774049856 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 6C01818005B2; Fri, 20 Mar 2026 23:37:36 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.80.37]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 08980180075C; Fri, 20 Mar 2026 23:37:33 +0000 (UTC) From: Lyude Paul To: linux-kernel@vger.kernel.org, Danilo Krummrich , rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Daniel Almeida , nouveau@lists.freedesktop.org, "Gary Guo" , "Miguel Ojeda" , "Alice Ryhl" , "Simona Vetter" , "Shankari Anand" , "Maxime Ripard" , "David Airlie" , "Benno Lossin" , "Asahi Lina" , "Lyude Paul" Subject: [PATCH v6 5/5] rust/drm/gem: Use DeviceContext with GEM objects Date: Fri, 20 Mar 2026 19:34:30 -0400 Message-ID: <20260320233645.950190-6-lyude@redhat.com> In-Reply-To: <20260320233645.950190-1-lyude@redhat.com> References: <20260320233645.950190-1-lyude@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Now that we have the ability to represent the context in which a DRM device is in at compile-time, we can start carrying around this context with GEM object types in order to allow a driver to safely create GEM objects before a DRM device has registered with userspace. Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida --- V4: * Add a comment to explain the Uninit DeviceContext usage in the GEM object vtable (tl;dr: the DeviceContext is meaningless here) V5: * Improve invariant comment above drm_gem_object_init() drivers/gpu/drm/nova/driver.rs | 2 +- drivers/gpu/drm/nova/gem.rs | 11 ++++--- drivers/gpu/drm/tyr/driver.rs | 2 +- drivers/gpu/drm/tyr/gem.rs | 10 ++++-- rust/kernel/drm/device.rs | 18 ++++++----- rust/kernel/drm/driver.rs | 2 +- rust/kernel/drm/gem/mod.rs | 57 ++++++++++++++++++++++------------ 7 files changed, 67 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 8cea5f68c3b04..2c13261450406 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -67,7 +67,7 @@ fn probe(adev: &auxiliary::Device, _info: &Self::Id= Info) -> impl PinInit; + type Object =3D gem::Object; =20 const INFO: drm::DriverInfo =3D INFO; =20 diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs index 6ccfa5da57617..f6e98b9db58d8 100644 --- a/drivers/gpu/drm/nova/gem.rs +++ b/drivers/gpu/drm/nova/gem.rs @@ -2,7 +2,7 @@ =20 use kernel::{ drm, - drm::{gem, gem::BaseObject}, + drm::{gem, gem::BaseObject, DeviceContext}, page, prelude::*, sync::aref::ARef, @@ -20,20 +20,23 @@ pub(crate) struct NovaObject {} impl gem::DriverObject for NovaObject { type Driver =3D NovaDriver; =20 - fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit { + fn new(_dev: &NovaDevice, _size: usize) -> im= pl PinInit { try_pin_init!(NovaObject {}) } } =20 impl NovaObject { /// Create a new DRM GEM object. - pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result>> { + pub(crate) fn new( + dev: &NovaDevice, + size: usize, + ) -> Result>> { if size =3D=3D 0 { return Err(EINVAL); } let aligned_size =3D page::page_align(size).ok_or(EINVAL)?; =20 - gem::Object::new(dev, aligned_size) + gem::Object::::new(dev, aligned_size) } =20 /// Look up a GEM object handle for a `File` and return an `ObjectRef`= for it. diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 76c5aed1171b1..157329fd09ea7 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -189,7 +189,7 @@ fn drop(self: Pin<&mut Self>) { impl drm::Driver for TyrDrmDriver { type Data =3D TyrDrmDeviceData; type File =3D TyrDrmFileData; - type Object =3D drm::gem::Object; + type Object =3D drm::gem::Object; =20 const INFO: drm::DriverInfo =3D INFO; =20 diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index 5cd0cd9585e8d..7499c38c7cde4 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 or MIT =20 use kernel::{ - drm::gem, + drm::{ + gem, + DeviceContext, // + }, prelude::*, // }; =20 @@ -17,7 +20,10 @@ pub(crate) struct TyrObject {} impl gem::DriverObject for TyrObject { type Driver =3D TyrDrmDriver; =20 - fn new(_dev: &TyrDrmDevice, _size: usize) -> impl PinInit= { + fn new( + _dev: &TyrDrmDevice, + _size: usize, + ) -> impl PinInit { try_pin_init!(TyrObject {}) } } diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 64b1089ba00d4..9842556efd8f8 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -163,13 +163,17 @@ impl UnregisteredDevice { master_set: None, master_drop: None, debugfs_init: None, - gem_create_object: T::Object::ALLOC_OPS.gem_create_object, - prime_handle_to_fd: T::Object::ALLOC_OPS.prime_handle_to_fd, - prime_fd_to_handle: T::Object::ALLOC_OPS.prime_fd_to_handle, - gem_prime_import: T::Object::ALLOC_OPS.gem_prime_import, - gem_prime_import_sg_table: T::Object::ALLOC_OPS.gem_prime_import_s= g_table, - dumb_create: T::Object::ALLOC_OPS.dumb_create, - dumb_map_offset: T::Object::ALLOC_OPS.dumb_map_offset, + + // Ignore the Uninit DeviceContext below. It is only provided beca= use it is required by the + // compiler, and it is not actually used by these functions. + gem_create_object: T::Object::::ALLOC_OPS.gem_create_objec= t, + prime_handle_to_fd: T::Object::::ALLOC_OPS.prime_handle_to= _fd, + prime_fd_to_handle: T::Object::::ALLOC_OPS.prime_fd_to_han= dle, + gem_prime_import: T::Object::::ALLOC_OPS.gem_prime_import, + gem_prime_import_sg_table: T::Object::::ALLOC_OPS.gem_prim= e_import_sg_table, + dumb_create: T::Object::::ALLOC_OPS.dumb_create, + dumb_map_offset: T::Object::::ALLOC_OPS.dumb_map_offset, + show_fdinfo: None, fbdev_probe: None, =20 diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index cfb8884ece700..e6893f089733d 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -110,7 +110,7 @@ pub trait Driver { type Data: Sync + Send; =20 /// The type used to manage memory for this driver. - type Object: AllocImpl; + type Object: AllocImpl; =20 /// The type used to represent a DRM File (client) type File: drm::file::DriverFile; diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index ad74c5159f725..1bae623d74492 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -8,6 +8,10 @@ bindings, drm::{ self, + device::{ + DeviceContext, + Registered, // + }, driver::{ AllocImpl, AllocOps, // @@ -22,6 +26,7 @@ types::Opaque, }; use core::{ + marker::PhantomData, ops::Deref, ptr::NonNull, // }; @@ -36,7 +41,8 @@ /// A type alias for retrieving the current [`AllocImpl`] for a given [`Dr= iverObject`]. /// /// [`Driver`]: drm::Driver -pub type DriverAllocImpl =3D <::Driver as drm::Drive= r>::Object; +pub type DriverAllocImpl =3D + <::Driver as drm::Driver>::Object; =20 /// GEM object functions, which must be implemented by drivers. pub trait DriverObject: Sync + Send + Sized { @@ -44,7 +50,10 @@ pub trait DriverObject: Sync + Send + Sized { type Driver: drm::Driver; =20 /// Create a new driver data object for a GEM object of a given size. - fn new(dev: &drm::Device, size: usize) -> impl PinInit; + fn new( + dev: &drm::Device, + size: usize, + ) -> impl PinInit; =20 /// Open a new handle to an existing object, associated with a File. fn open(_obj: &DriverAllocImpl, _file: &DriverFile) -> Res= ult { @@ -78,9 +87,12 @@ extern "C" fn open_callback( // SAFETY: `open_callback` is only ever called with a valid pointer to= a `struct drm_file`. let file =3D unsafe { DriverFile::::from_raw(raw_file) }; =20 - // SAFETY: `open_callback` is specified in the AllocOps structure for = `DriverObject`, - // ensuring that `raw_obj` is contained within a `DriverObject` - let obj =3D unsafe { <::Object as IntoGEMObj= ect>::from_raw(raw_obj) }; + // SAFETY: + // * `open_callback` is specified in the AllocOps structure for `Drive= rObject`, ensuring that + // `raw_obj` is contained within a `DriverAllocImpl` + // * It is only possible for `open_callback` to be called after device= registration, ensuring + // that the object's device is in the `Registered` state. + let obj: &DriverAllocImpl =3D unsafe { IntoGEMObject::from_raw(raw_= obj) }; =20 match T::open(obj, file) { Err(e) =3D> e.to_errno(), @@ -97,12 +109,12 @@ extern "C" fn close_callback( =20 // SAFETY: `close_callback` is specified in the AllocOps structure for= `Object`, ensuring // that `raw_obj` is indeed contained within a `Object`. - let obj =3D unsafe { <::Object as IntoGEMObj= ect>::from_raw(raw_obj) }; + let obj: &DriverAllocImpl =3D unsafe { IntoGEMObject::from_raw(raw_= obj) }; =20 T::close(obj, file); } =20 -impl IntoGEMObject for Object { +impl IntoGEMObject for Object= { fn as_raw(&self) -> *mut bindings::drm_gem_object { self.obj.get() } @@ -110,7 +122,7 @@ fn as_raw(&self) -> *mut bindings::drm_gem_object { unsafe fn from_raw<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a= Self { // SAFETY: `obj` is guaranteed to be in an `Object` via the saf= ety contract of this // function - unsafe { &*crate::container_of!(Opaque::cast_from(self_ptr), Objec= t, obj) } + unsafe { &*crate::container_of!(Opaque::cast_from(self_ptr), Objec= t, obj) } } } =20 @@ -127,7 +139,7 @@ fn size(&self) -> usize { fn create_handle(&self, file: &drm::File) -> Result where Self: AllocImpl, - D: drm::Driver, + D: drm::Driver =3D Self, File =3D F>, F: drm::file::DriverFile, { let mut handle: u32 =3D 0; @@ -142,7 +154,7 @@ fn create_handle(&self, file: &drm::File) -> R= esult fn lookup_handle(file: &drm::File, handle: u32) -> Result> where Self: AllocImpl, - D: drm::Driver, + D: drm::Driver =3D Self, File =3D F>, F: drm::file::DriverFile, { // SAFETY: The arguments are all valid per the type invariants. @@ -182,16 +194,18 @@ impl BaseObject for T {} /// /// # Invariants /// -/// - `self.obj` is a valid instance of a `struct drm_gem_object`. +/// * `self.obj` is a valid instance of a `struct drm_gem_object`. +/// * Any type invariants of `Ctx` apply to the parent DRM device for this= GEM object. #[repr(C)] #[pin_data] -pub struct Object { +pub struct Object { obj: Opaque, #[pin] data: T, + _ctx: PhantomData, } =20 -impl Object { +impl Object { const OBJECT_FUNCS: bindings::drm_gem_object_funcs =3D bindings::drm_g= em_object_funcs { free: Some(Self::free_callback), open: Some(open_callback::), @@ -211,11 +225,12 @@ impl Object { }; =20 /// Create a new GEM object. - pub fn new(dev: &drm::Device, size: usize) -> Result> { + pub fn new(dev: &drm::Device, size: usize) -> Result> { let obj: Pin> =3D KBox::pin_init( try_pin_init!(Self { obj: Opaque::new(bindings::drm_gem_object::default()), data <- T::new(dev, size), + _ctx: PhantomData, }), GFP_KERNEL, )?; @@ -224,6 +239,8 @@ pub fn new(dev: &drm::Device, size: usize) -= > Result> { unsafe { (*obj.as_raw()).funcs =3D &Self::OBJECT_FUNCS }; =20 // SAFETY: The arguments are all valid per the type invariants. + // INVARIANT: `dev` and the GEM object are in the same state at th= e moment, and upgrading + // the typestate in `dev` will not carry over to the GEM object. to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj= .obj.get(), size) })?; =20 // SAFETY: We will never move out of `Self` as `ARef` is alw= ays treated as pinned. @@ -237,13 +254,15 @@ pub fn new(dev: &drm::Device, size: usize)= -> Result> { } =20 /// Returns the `Device` that owns this GEM object. - pub fn dev(&self) -> &drm::Device { + pub fn dev(&self) -> &drm::Device { // SAFETY: // - `struct drm_gem_object.dev` is initialized and valid for as l= ong as the GEM // object lives. // - The device we used for creating the gem object is passed as &= drm::Device to // Object::::new(), so we know that `T::Driver` is the right = generic parameter to use // here. + // - Any type invariants of `Ctx` are upheld by using the same `Ct= x` for the `Device` we + // return. unsafe { drm::Device::from_raw((*self.as_raw()).dev) } } =20 @@ -269,7 +288,7 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem= _object) { } =20 // SAFETY: Instances of `Object` are always reference-counted. -unsafe impl crate::sync::aref::AlwaysRefCounted for Objec= t { +unsafe impl AlwaysRefCounted for Obje= ct { fn inc_ref(&self) { // SAFETY: The existence of a shared reference guarantees that the= refcount is non-zero. unsafe { bindings::drm_gem_object_get(self.as_raw()) }; @@ -284,9 +303,9 @@ unsafe fn dec_ref(obj: NonNull) { } } =20 -impl super::private::Sealed for Object {} +impl super::private::Sealed for Objec= t {} =20 -impl Deref for Object { +impl Deref for Object { type Target =3D T; =20 fn deref(&self) -> &Self::Target { @@ -294,7 +313,7 @@ fn deref(&self) -> &Self::Target { } } =20 -impl AllocImpl for Object { +impl AllocImpl for Object { type Driver =3D T::Driver; =20 const ALLOC_OPS: AllocOps =3D AllocOps { --=20 2.53.0