From nobody Mon Jun 8 05:25:26 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 BBE0038B7CD for ; Thu, 4 Jun 2026 19:28:56 +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=1780601338; cv=none; b=bhUbtluYBQ6zFva5AMYkRrPH10ULA3iismLAtOp4adpXxDKNM7qVe059aTIdEByu8/B92NGhkffjtccUC2WVBZwi3QY8GK1fBv9u40HNoTur0PSA+hmSEqj7tIgWx/7JmVJNYcsVo4wObQSKGEbG/EFBRV5mDQFjhAaPI7WyuMw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780601338; c=relaxed/simple; bh=N9Zkr/AWzPMGNFm1Ln4LhJkt/EZGyo53j68P4QRjIxU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l57jIHTxGDmbj3OXUyQ9q334AH07/BG5fBn2yWI/Ug2tZpzuGe7z1rMLG3Wcu/mAbuY38zT7zXF+et4boP/Q6IoQ0cXLxiBMWEtqXqYM3f342DZWpdff6N11ej0b6gbmsHNIjqh2CczOvR23aylWkfkgzAZ7PgUTS3u2Tjusz6k= 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=CovXhcqc; 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="CovXhcqc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780601336; 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=r7Z/C3WKzX9d11zFSyCLO1QOj5Cg/fxzu0wnbss0mqw=; b=CovXhcqcQMQn+6+QDrWmMFXIuzCgVvCdZiXThiYAzuzb39bjbMo5ZbVcyXHNnXIBTfHSb/ 28UuwvlxCNzB+2OXhGFSErznZSXneVPQeRZsErYEaHwgtfFCYVeR56tCTAr1ebYT611v67 +p+vh2ROspOPiSD0T1wsxPvaIT0RB4Y= 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-19-zgX0bRHXN9yWCnDpzcJjUQ-1; Thu, 04 Jun 2026 15:28:54 -0400 X-MC-Unique: zgX0bRHXN9yWCnDpzcJjUQ-1 X-Mimecast-MFC-AGG-ID: zgX0bRHXN9yWCnDpzcJjUQ_1780601331 Received: from mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.95]) (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 033151800347; Thu, 4 Jun 2026 19:28:51 +0000 (UTC) Received: from GoldenWind.lan (unknown [10.22.65.199]) by mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 562BF763; Thu, 4 Jun 2026 19:28:47 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org Cc: Alexandre Courbot , Gary Guo , =?UTF-8?q?Christian=20K=C3=B6nig?= , driver-core@lists.linux.dev, Miguel Ojeda , Maarten Lankhorst , Alice Ryhl , Simona Vetter , linux-kernel@vger.kernel.org, Sumit Semwal , linux-media@vger.kernel.org, "Rafael J . Wysocki" , Thomas Zimmermann , Maxime Ripard , David Airlie , Benno Lossin , linaro-mm-sig@lists.linaro.org, Danilo Krummrich , Mukesh Kumar Chaurasiya , Asahi Lina , Daniel Almeida , Lyude Paul , Greg Kroah-Hartman Subject: [PATCH v18 1/4] rust: drm: gem: shmem: Add DmaResvGuard helper Date: Thu, 4 Jun 2026 15:24:28 -0400 Message-ID: <20260604192740.659240-2-lyude@redhat.com> In-Reply-To: <20260604192740.659240-1-lyude@redhat.com> References: <20260604192740.659240-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.6 on 10.30.177.95 Content-Type: text/plain; charset="utf-8" Just a temporary holdover to make locking/unlocking the dma_resv lock much easier. Signed-off-by: Lyude Paul Co-authored-by: Alexandre Courbot Signed-off-by: Alexandre Courbot --- V17: * Fix format of commit message title rust/kernel/drm/gem/shmem.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 084b798ce795b..650c34dd8b7a4 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -30,7 +30,10 @@ Deref, DerefMut, // }, - ptr::NonNull, // + ptr::{ + self, + NonNull, // + }, }; use gem::{ BaseObjectPrivate, @@ -244,3 +247,29 @@ impl driver::AllocI= mpl for Object { dumb_map_offset: None, }; } + +/// Private helper-type for holding the `dma_resv` object for a GEM shmem = object. +/// +/// When this is dropped, the `dma_resv` lock is dropped as well. +/// +// TODO: This should be replace with a WwMutex equivalent once we have suc= h bindings in the kernel. +struct DmaResvGuard<'a, T: DriverObject, C: DeviceContext =3D Registered>(= &'a Object); + +impl<'a, T: DriverObject, C: DeviceContext> DmaResvGuard<'a, T, C> { + #[inline(always)] + #[expect(unused)] + fn new(obj: &'a Object) -> Self { + // SAFETY: This lock is initialized throughout the lifetime of `ob= ject`. + unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut= ()) }; + + Self(obj) + } +} + +impl<'a, T: DriverObject, C: DeviceContext> Drop for DmaResvGuard<'a, T, C= > { + #[inline(always)] + fn drop(&mut self) { + // SAFETY: We are releasing the lock grabbed during the creation o= f this object. + unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) }; + } +} --=20 2.54.0 From nobody Mon Jun 8 05:25:26 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 967BE3C585F for ; Thu, 4 Jun 2026 19:29:05 +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=1780601347; cv=none; b=Ie0VYpLvYFFYlcLVMtUx98kfe+wVRqUa3OCdzF0inaFQzRl0RwSnUD1SHhHCLFY513dNTMjm+jgugH3LuNbg4Yh07+UdYtinsUQ6HqZ+0jwMqDj/oaAUmpxBVs0KFAhPrG96zM5X37VUukdTbyQHiyALDR5YU7HzX9oNODRj2aU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780601347; c=relaxed/simple; bh=HLDYZP0lehESbsRi+pGlDC6Lfm2TaTyerFCFRIDfqSk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SznNS9xAROoSdQNKYXhLQd9Vjq0CE/jrvCmiYe5QhQL8JLu9kcbX81EFd4l1mfg7XsEOcyLG9PCo/uLs3iu0tfkXubrH4qA++ZdmEh4H1X5Kd/796W5tlAZUpGEw4MSdugbLO/LgvTr7ZPcmGUSFJaqcrDTHciPgJzxAJIj1jEk= 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=SWMmWQ9Z; 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="SWMmWQ9Z" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780601344; 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=3E5VGwkkVj6EfELSmGE+fCDhpjnsN/r1pe+Wo/sQdVo=; b=SWMmWQ9ZGWL1aH9f/bSxOI1RrFMToHiJoIxgfKDQz5nGjkacL88xiM6ungE/EYHVikPnsq NHAUUuZG5aekGRzDP4Wjq5WMYEnwtA/oaXPKiRhFF0/Slz8+iNVxOZPpwHnkcBxXq3cWSS 8zv7kGI5O4SWfRnscnXjfyD4u/Jx4fg= 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-271-zQ4TWdJ1OM2odFtJ0yZpAg-1; Thu, 04 Jun 2026 15:28:58 -0400 X-MC-Unique: zQ4TWdJ1OM2odFtJ0yZpAg-1 X-Mimecast-MFC-AGG-ID: zQ4TWdJ1OM2odFtJ0yZpAg_1780601335 Received: from mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.95]) (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 CC091195609D; Thu, 4 Jun 2026 19:28:54 +0000 (UTC) Received: from GoldenWind.lan (unknown [10.22.65.199]) by mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5E1FA414; Thu, 4 Jun 2026 19:28:51 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org Cc: Alexandre Courbot , Gary Guo , =?UTF-8?q?Christian=20K=C3=B6nig?= , driver-core@lists.linux.dev, Miguel Ojeda , Maarten Lankhorst , Alice Ryhl , Simona Vetter , linux-kernel@vger.kernel.org, Sumit Semwal , linux-media@vger.kernel.org, "Rafael J . Wysocki" , Thomas Zimmermann , Maxime Ripard , David Airlie , Benno Lossin , linaro-mm-sig@lists.linaro.org, Danilo Krummrich , Mukesh Kumar Chaurasiya , Asahi Lina , Daniel Almeida , Lyude Paul , Greg Kroah-Hartman Subject: [PATCH v18 2/4] rust: drm: gem: shmem: Add vmap functions Date: Thu, 4 Jun 2026 15:24:29 -0400 Message-ID: <20260604192740.659240-3-lyude@redhat.com> In-Reply-To: <20260604192740.659240-1-lyude@redhat.com> References: <20260604192740.659240-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.6 on 10.30.177.95 Content-Type: text/plain; charset="utf-8" One of the more obvious use cases for gem shmem objects is the ability to create mappings into their contents. So, let's hook this up in our rust bindings. Signed-off-by: Lyude Paul Reviewed-by: Alexandre Courbot --- V7: * Switch over to the new iosys map bindings that use the Io trait V8: * Get rid of iosys_map bindings for now, only support non-iomem types * s/as_shmem()/as_raw_shmem() V9: * Get rid of some outdated comments I missed * Add missing SIZE check to raw_vmap() * Add a proper unit test that ensures that we actually validate SIZE at compile-time. Turns out it takes only 34 lines to make a boilerplate DRM driver for a kunit test :) * Add unit tests * Add some missing #[inline]s V10: * Correct issue with iomem error path We previously called raw_vunmap() if we got an iomem allocation, but raw_vunmap() was written such that it assumed all allocations were sysmem allocations. Fix this by just making raw_vunmap() accept a iosys_map. V11: * Use Alexandre's clever solution to remove the macros we were using for maintaining two different VMap types. * Change the order of items in Object to ensure that sgt_res is always dropped before obj. * Fix typo in Object.raw_vmap() * s/raw_vmap()/make_vmap()/ Deduplicate code a bit more as well by using more generics here V15: * Add these patches back * We only have one VMap type now! * Use ObjectConfig::default() in unit tests since we unbroke it. V16: * Fix huge rebase error I made and did not notice that squashed 1.5 patches together that were definitely not supposed to be squashed * Update old commit message V17: * Rebase * Fix format of commit message title rust/kernel/drm/gem/shmem.rs | 312 ++++++++++++++++++++++++++++++++++- 1 file changed, 311 insertions(+), 1 deletion(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 650c34dd8b7a4..1f05a5bc5fe66 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -20,12 +20,19 @@ Registered, // }, error::to_result, + io::{ + Io, + IoCapable, + IoKnownSize, // + }, prelude::*, sync::aref::ARef, types::Opaque, // }; use core::{ + ffi::c_void, marker::PhantomData, + mem::MaybeUninit, // ops::{ Deref, DerefMut, // @@ -36,6 +43,7 @@ }, }; use gem::{ + BaseObject, BaseObjectPrivate, DriverObject, IntoGEMObject, // @@ -197,6 +205,80 @@ extern "C" fn free_callback(obj: *mut bindings::drm_ge= m_object) { // SAFETY: We're recovering the Kbox<> we created in gem_create_ob= ject() let _ =3D unsafe { KBox::from_raw(this) }; } + + /// Attempt to create a vmap from the gem object, and confirm the size= of said vmap. + fn make_vmap<'a, R, const SIZE: usize>(&'a self) -> Result> + where + R: Deref + From<&'a Self>, + { + // INVARIANT: We check here that the gem object is at least as lar= ge as `SIZE`. + if self.size() < SIZE { + return Err(ENOSPC); + } + + let mut map: MaybeUninit =3D MaybeUninit::uni= nit(); + let guard =3D DmaResvGuard::new(self); + + // SAFETY: drm_gem_shmem_vmap can be called with the DMA reservati= on lock held + to_result(unsafe { + bindings::drm_gem_shmem_vmap_locked(self.as_raw_shmem(), map.a= s_mut_ptr()) + })?; + + // Drop the guard explicitly here, since we may need to call raw_v= unmap() (which re-acquires + // the lock). + drop(guard); + + // SAFETY: The call to drm_gem_shmem_vmap_locked succeeded above, = so we are guaranteed that + // map is properly initialized. + let map =3D unsafe { map.assume_init() }; + + // XXX: We don't currently support iomem allocations + if map.is_iomem { + // SAFETY: + // - The vmap operation above succeeded, guaranteeing that `ma= p` points to a valid + // memory mapping. + // - We checked that this is an iomem allocation, making it sa= fe to read vaddr_iomem + unsafe { self.raw_vunmap(map) }; + + Err(ENOTSUPP) + } else { + Ok(VMap { + // SAFETY: We checked that this is not an iomem allocation= , making it safe to read + // vaddr + addr: unsafe { map.__bindgen_anon_1.vaddr }, + owner: self.into(), + }) + } + } + + /// Unmap a vmap from the gem object. + /// + /// # Safety + /// + /// - The caller promises that `map` is a valid vmap on this gem objec= t. + /// - The caller promises that the memory pointed to by map will no lo= nger be accesed through + /// this instance. + unsafe fn raw_vunmap(&self, mut map: bindings::iosys_map) { + let _guard =3D DmaResvGuard::new(self); + + // SAFETY: + // - This function is safe to call with the DMA reservation lock h= eld. + // - Our `ARef` is proof that the underlying gem object here is in= itialized and thus safe to + // dereference. + unsafe { bindings::drm_gem_shmem_vunmap_locked(self.as_raw_shmem()= , &mut map) }; + } + + /// Creates and returns a virtual kernel memory mapping for this objec= t. + #[inline] + pub fn vmap(&self) -> Result> { + self.make_vmap() + } + + /// Creates and returns an owned reference to a virtual kernel memory = mapping for this object. + #[inline] + pub fn owned_vmap(&self) -> Result> { + self.make_vmap() + } } =20 impl Deref for Object { @@ -257,7 +339,6 @@ impl driver::AllocIm= pl for Object { =20 impl<'a, T: DriverObject, C: DeviceContext> DmaResvGuard<'a, T, C> { #[inline(always)] - #[expect(unused)] fn new(obj: &'a Object) -> Self { // SAFETY: This lock is initialized throughout the lifetime of `ob= ject`. unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut= ()) }; @@ -273,3 +354,232 @@ fn drop(&mut self) { unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) }; } } + +macro_rules! impl_vmap_io_capable { + ($impl:ident, $ty:ty) =3D> { + impl IoCapable<$ty> for $impl + where + D: DriverObject, + C: DeviceContext, + R: Deref>, + { + #[inline(always)] + unsafe fn io_read(&self, address: usize) -> $ty { + let ptr =3D address as *mut $ty; + + // SAFETY: The safety contract of `io_read` guarantees tha= t address is a valid + // address within the bounds of `Self` of at least the siz= e of $ty, and is properly + // aligned. + unsafe { ptr::read(ptr) } + } + + #[inline(always)] + unsafe fn io_write(&self, value: $ty, address: usize) { + let ptr =3D address as *mut $ty; + + // SAFETY: The safety contract of `io_write` guarantees th= at address is a valid + // address within the bounds of `Self` of at least the siz= e of $ty, and is properly + // aligned. + unsafe { ptr::write(ptr, value) } + } + } + }; +} + +/// A reference to a virtual mapping for an shmem-based GEM object in kern= el address space. +/// +/// # Invariants +/// +/// - The size of `owner` is >=3D SIZE. +/// - The memory pointed to by addr remains valid at least until this obje= ct is dropped. +pub struct VMap +where + D: DriverObject, + C: DeviceContext, + R: Deref>, +{ + addr: *mut c_void, + owner: R, +} + +/// An alias type for a reference to a shmem-based GEM object's VMap. +pub type VMapRef<'a, D, C, const SIZE: usize =3D 0> =3D VMap, C, SIZE>; + +/// An alias type for an owned reference to a shmem-based GEM object's VMa= p. +pub type VMapOwned =3D VMap>, C, SIZE>; + +impl VMap +where + D: DriverObject, + C: DeviceContext, + R: Deref>, +{ + /// Borrows a reference to the object that owns this virtual mapping. + #[inline(always)] + pub fn owner(&self) -> &Object { + &self.owner + } +} + +impl Drop for VMap +where + D: DriverObject, + C: DeviceContext, + R: Deref>, +{ + #[inline(always)] + fn drop(&mut self) { + // SAFETY: + // - Our existence is proof that this map was previously created u= sing self.owner. + // - Since we are in Drop, we are guaranteed that no one will acce= ss the memory + // through this mapping after calling this. + unsafe { + self.owner.raw_vunmap(bindings::iosys_map { + is_iomem: false, + __bindgen_anon_1: bindings::iosys_map__bindgen_ty_1 { vadd= r: self.addr }, + }) + }; + } +} + +impl Io for VMap +where + D: DriverObject, + C: DeviceContext, + R: Deref>, +{ + #[inline(always)] + fn addr(&self) -> usize { + self.addr as usize + } + + #[inline(always)] + fn maxsize(&self) -> usize { + self.owner.size() + } +} + +impl IoKnownSize for VMap +where + D: DriverObject, + C: DeviceContext, + R: Deref>, +{ + const MIN_SIZE: usize =3D SIZE; +} + +impl_vmap_io_capable!(VMap, u8); +impl_vmap_io_capable!(VMap, u16); +impl_vmap_io_capable!(VMap, u32); +#[cfg(CONFIG_64BIT)] +impl_vmap_io_capable!(VMap, u64); + +#[kunit_tests(rust_drm_gem_shmem)] +mod tests { + use super::*; + use crate::{ + drm::{ + self, + UnregisteredDevice, // + }, + faux, + page::PAGE_SIZE, // + }; + + // The bare minimum needed to create a fake drm driver for kunit + + #[pin_data] + struct KunitData {} + struct KunitDriver; + struct KunitFile; + #[pin_data] + struct KunitObject {} + + const INFO: drm::DriverInfo =3D drm::DriverInfo { + major: 0, + minor: 0, + patchlevel: 0, + name: c"kunit", + desc: c"Kunit", + }; + + impl drm::file::DriverFile for KunitFile { + type Driver =3D KunitDriver; + + fn open(_dev: &drm::Device) -> Result>= > { + Ok(KBox::new(Self, GFP_KERNEL)?.into()) + } + } + + impl gem::DriverObject for KunitObject { + type Driver =3D KunitDriver; + type Args =3D (); + + fn new( + _dev: &drm::Device, + _size: usize, + _args: Self::Args, + ) -> impl PinInit { + try_pin_init!(KunitObject {}) + } + } + + #[vtable] + impl drm::Driver for KunitDriver { + type Data =3D KunitData; + type File =3D KunitFile; + type Object =3D Object; + + const INFO: drm::DriverInfo =3D INFO; + const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor] =3D &[]; + } + + fn create_drm_dev() -> Result<(faux::Registration, UnregisteredDevice<= KunitDriver>)> { + // Create a faux DRM device so we can test gem object creation. + let data =3D try_pin_init!(KunitData {}); + let dev =3D faux::Registration::new(c"Kunit", None)?; + let drm =3D UnregisteredDevice::new(dev.as_ref(), data)?; + + Ok((dev, drm)) + } + + #[test] + fn compile_time_vmap_sizes() -> Result { + let (_dev, drm) =3D create_drm_dev()?; + + let obj =3D Object::::new(&drm, PAGE_SIZE, ObjectC= onfig::default(), ())?; + + // Try creating a normal vmap + obj.vmap::()?; + + // Try creating a vmap that's smaller then the size we specified + obj.vmap::<{ PAGE_SIZE - 100 }>()?; + + // Make sure creating a vmap that's too large fails + assert!(obj.vmap::<{ PAGE_SIZE + 200 }>().is_err()); + + Ok(()) + } + + #[test] + fn vmap_io() -> Result { + let (_dev, drm) =3D create_drm_dev()?; + + let obj =3D Object::::new(&drm, PAGE_SIZE, ObjectC= onfig::default(), ())?; + + let vmap =3D obj.vmap::()?; + + vmap.write8(0xDE, 0x0); + assert_eq!(vmap.read8(0x0), 0xDE); + vmap.write32(0xFFFFFFFF, 0x20); + + assert_eq!(vmap.read32(0x20), 0xFFFFFFFF); + + assert_eq!(vmap.read8(0x20), 0xFF); + assert_eq!(vmap.read8(0x21), 0xFF); + assert_eq!(vmap.read8(0x22), 0xFF); + assert_eq!(vmap.read8(0x23), 0xFF); + + Ok(()) + } +} --=20 2.54.0 From nobody Mon Jun 8 05:25:26 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 5C3823C5842 for ; Thu, 4 Jun 2026 19:29:06 +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=1780601347; cv=none; b=SZp24j0gSPBwCUDGWdLzQ0MeoCGkc5GVLn12fUuHzAOsQwj2BM15L/UdiWpzukxQoGl7fNGYZcx7cPIIDmcfkWvQ7YF6khcmFxrbXB8Il7u5Uk3nuy+U+5o88zOAtkykZdQlYs4II6VpLiOQLfUUujyynS6bQaOVBU8Pzs6XMVk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780601347; c=relaxed/simple; bh=DD+1DvBHIa2PD9GHPH9T6cV9e+RpfxknnwEPDqeurlo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c1XlF+v/Di2nEIapcmpFIYAsYbpapS9UJg08zChZKMAD5MZm6jBdfJMwPnMxRGoyYHNEgpDWyUmOsOvDezWHQjKF7azD5JgQrUFeHVOc5b5GDJjmkzGmLlYCPLQQgfq2GSV72q9ik1fQY7faH5UiN+2e5FV+DRi5PWxkT4lWjCo= 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=TKy6N5af; 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="TKy6N5af" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780601345; 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=QlYcsDX/fFbrYtcqnwAP7Gnuoxt4BlnnFKxcvfLkLbM=; b=TKy6N5afP1pE0qMnexE9v0jgH2rN+mcZBHmKZfoVpOqAc/innzjcmeqd09hmwYBZm00Wij 7Yg4B3uHvj5UHeYhHKvpdLREOwQj1yGk+LPAl+6NYQ00qALCHKNbQeKDndqKhzSfqS8iQL J70O7q3rpO6ApNlH49fPJy8zvhopcSM= Received: from mx-prod-mc-05.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-503-qx9TbXrVNYuVCuw1QEsuyw-1; Thu, 04 Jun 2026 15:29:01 -0400 X-MC-Unique: qx9TbXrVNYuVCuw1QEsuyw-1 X-Mimecast-MFC-AGG-ID: qx9TbXrVNYuVCuw1QEsuyw_1780601338 Received: from mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.95]) (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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 50A02195608E; Thu, 4 Jun 2026 19:28:58 +0000 (UTC) Received: from GoldenWind.lan (unknown [10.22.65.199]) by mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0903B763; Thu, 4 Jun 2026 19:28:54 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org Cc: Alexandre Courbot , Gary Guo , =?UTF-8?q?Christian=20K=C3=B6nig?= , driver-core@lists.linux.dev, Miguel Ojeda , Maarten Lankhorst , Alice Ryhl , Simona Vetter , linux-kernel@vger.kernel.org, Sumit Semwal , linux-media@vger.kernel.org, "Rafael J . Wysocki" , Thomas Zimmermann , Maxime Ripard , David Airlie , Benno Lossin , linaro-mm-sig@lists.linaro.org, Danilo Krummrich , Mukesh Kumar Chaurasiya , Asahi Lina , Daniel Almeida , Lyude Paul , Greg Kroah-Hartman Subject: [PATCH v18 3/4] rust: faux: Allow retrieving a bound Device Date: Thu, 4 Jun 2026 15:24:30 -0400 Message-ID: <20260604192740.659240-4-lyude@redhat.com> In-Reply-To: <20260604192740.659240-1-lyude@redhat.com> References: <20260604192740.659240-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.6 on 10.30.177.95 Content-Type: text/plain; charset="utf-8" When writing up some rust code that used faux devices for unit testing, I noticed that we never actually added the Bound device context to faux::Registration's AsRef implementation. This being said: the Registration object itself is proof that a driver is bound to the device - so this should be safe. Signed-off-by: Lyude Paul --- V18: - Add notes from Danilo to safety comment. rust/kernel/faux.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs index 43b4974f48cd2..20ab638885354 100644 --- a/rust/kernel/faux.rs +++ b/rust/kernel/faux.rs @@ -25,7 +25,8 @@ /// /// # Invariants /// -/// `self.0` always holds a valid pointer to an initialized and registered= [`struct faux_device`]. +/// - `self.0` always holds a valid pointer to an initialized and register= ed [`struct faux_device`]. +/// - This object is proof that the object described by this `Registration= ` is bound to a device. /// /// [`struct faux_device`]: srctree/include/linux/device/faux.h pub struct Registration(NonNull); @@ -59,10 +60,15 @@ fn as_raw(&self) -> *mut bindings::faux_device { } } =20 -impl AsRef for Registration { - fn as_ref(&self) -> &device::Device { - // SAFETY: The underlying `device` in `faux_device` is guaranteed = by the C API to be - // a valid initialized `device`. +impl AsRef> for Registration { + fn as_ref(&self) -> &device::Device { + // SAFETY: + // - The underlying `device` in `faux_device` is guaranteed by the= C API to be a valid + // initialized `device`. + // - faux_match() always returns 1, and probe runs synchronously (= PROBE_FORCE_SYNCHRONOUS). + // - suppress_bind_attrs =3D true on faux_driver prevents userspac= e-triggered unbind via sysfs + // - mem::forget(Registration) is not a problem; if the Registrati= on is leaked, the faux + // device stays bound forever. unsafe { device::Device::from_raw(addr_of_mut!((*self.as_raw()).de= v)) } } } --=20 2.54.0 From nobody Mon Jun 8 05:25:26 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 9B6EB3C3C0F for ; Thu, 4 Jun 2026 19:29:10 +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=1780601352; cv=none; b=j5jJCX62GDvFaKgSVAIw2Y25b2ssitRF7tUxQrW6kKL/jI+gPQ/YZf65bflPMJROS9TVUnUbwi5uoNQiDzOi8YNfdTwdGRbNyjADOIGuBBspt4Zb32is3sHfQz4cD5v2x4UqiDt8m7cYaBcstgQoZ/s2CGpAGpD8PDyYDxHYR3k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780601352; c=relaxed/simple; bh=4VpO1uKJHjqxXr/otGUFPMhwkZPAJaalEOsZ/tRSutg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=faWJcBMm0FXV+cdXDflvf/TzUUsBcUgiozgyDx50Yu/h3tNthTOBLS44xeUenHbaErPKWZSbpx7okS7qFHX/pDhJXzzV/4diM87if+sVKMX88fJgFsrbRgnAZdmN3tlM6UjjtsN8eAIyCs53+CMRMpUXGPgF8DjnB3EOeG5q2/E= 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=BLgCvs9a; 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="BLgCvs9a" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780601349; 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=InqRthNbxREjooXND9dAPLNy7iYy4wyMC/bsA8GkDY4=; b=BLgCvs9a8UOGshoXRFS+Bc3rv/PSuzkm/jh7BsYmEURLnQpZQdJMwpCh5jMjkELz/pDMTK HNaNrnWTjRJ1wAIpkrEl8QJ8v7Wd8bPolfeEqBD982pC39Q7k55yJ99+5+P1pStvD+NuHW q8pz6ijs4M8cDHvC0OtOX/0VoEyVzOk= 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-479-e-rJoRE0MKKTswUgDmg0hw-1; Thu, 04 Jun 2026 15:29:05 -0400 X-MC-Unique: e-rJoRE0MKKTswUgDmg0hw-1 X-Mimecast-MFC-AGG-ID: e-rJoRE0MKKTswUgDmg0hw_1780601342 Received: from mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.95]) (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 2741018005A9; Thu, 4 Jun 2026 19:29:02 +0000 (UTC) Received: from GoldenWind.lan (unknown [10.22.65.199]) by mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id ABD77414; Thu, 4 Jun 2026 19:28:58 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org Cc: Alexandre Courbot , Gary Guo , =?UTF-8?q?Christian=20K=C3=B6nig?= , driver-core@lists.linux.dev, Miguel Ojeda , Maarten Lankhorst , Alice Ryhl , Simona Vetter , linux-kernel@vger.kernel.org, Sumit Semwal , linux-media@vger.kernel.org, "Rafael J . Wysocki" , Thomas Zimmermann , Maxime Ripard , David Airlie , Benno Lossin , linaro-mm-sig@lists.linaro.org, Danilo Krummrich , Mukesh Kumar Chaurasiya , Asahi Lina , Daniel Almeida , Lyude Paul , Greg Kroah-Hartman Subject: [PATCH v18 4/4] rust: drm: gem: Introduce shmem::Object::sg_table() Date: Thu, 4 Jun 2026 15:24:31 -0400 Message-ID: <20260604192740.659240-5-lyude@redhat.com> In-Reply-To: <20260604192740.659240-1-lyude@redhat.com> References: <20260604192740.659240-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.6 on 10.30.177.95 Content-Type: text/plain; charset="utf-8" In order to do this, we need to be careful to ensure that any interface we expose for scatterlists ensures that any mappings created from one are destroyed on driver-unbind. To do this, we introduce a Devres resource into shmem::Object that we use in order to ensure that we release any SGTable mappings on driver-unbind. There's some other slightly unfortunate caveats of this: * Drivers don't have explicit control at the moment over when unmapping happens (which is exactly the same as the C side atm, so it might not be a problem). * We can't just return `SGTableMap` to the user through an Arc to attempt to fix the last caveat - because that implies the gem object would need to hold a reference count to the scatterlist mapping, which just leaves us with the same problem. Signed-off-by: Lyude Paul --- V3: * Rename OwnedSGTable to shmem::SGTable. Since the current version of the SGTable abstractions now has a `Owned` and `Borrowed` variant, I think renaming this to shmem::SGTable makes things less confusing. We do however, keep the name of owned_sg_table() as-is. V4: * Clarify safety comments for SGTable to explain why the object is thread-safe. * Rename from SGTableRef to SGTable V10: * Use Devres in order to ensure that SGTables are revocable, and are unmapped on driver-unbind. V11: * s/create_sg_table()/get_sg_table() * Get rid of extraneous `ret =3D ` in shmem::Object::get_sg_table() V12: * Actually move sgt_res in this patch and not the next one V13: * Use DmaResvGuard suggestion from Alexander * Use Alexander's (much better) solution for get_sg_table() * Use SetOnce instead of UnsafeCell * s/SGTableRef/SGTableMap * Fix typo in SGTableMap documentation * Create fallible constructor for SGTableMap * Don't reuse dma_resv lock for protecting Object contents, just use Mutex + SetOnce * Drop use of drm_gem_shmem_get_pages_sgt_locked(), since we don't need to hold the dma_resv lock ourselves for anything but this function. * Check that the device we receive in the bounds for sg_table() and owned_sg_table() that said Device is in fact, the correct device. * Remove redundant docs in owned_sg_table(), just point it back to sg_table(). * Implement Deborah's suggestion to fix double-free in free_callback() * Restore original order of Object * Fix doc typo for SGTableMap V14: * Use new InitOnce container over the Mutex/SetOnce horror show we had before. * Start using LazyInit container for storing Devres for sgt unmap * Add some kunit tests for sg_table (not sure why I didn't do this before) using some of the boilerplate code leftover from the vmap bindings * Get rid of the owned SGTable variant for now, we'll add it back in a future patch if people actually need it. * Use new LazyInit container from me to get rid of the horrid Mutex> mess. * Add the best we can do for unit tests w/r/t SGTable at the moment V16: * Get rid of LazyInit, go back to SetOnce, use trick that Alice recommended that is a lot cleaner. * Fix horrid rebasing mistake V17: * Rebase * Fix missing safety comment in free_callback() (we forgot to justify why &mut is safe in `unsafe { &mut (*this).sgt_res }.reset()`) V18: * Use ManuallyDrop instead of SetOnce::reset() rust/kernel/drm/gem/shmem.rs | 172 +++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 10 deletions(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 1f05a5bc5fe66..a9970fca1d298 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -11,6 +11,11 @@ =20 use crate::{ container_of, + device::{ + self, + Bound, // + }, + devres::*, drm::{ driver, gem, @@ -19,20 +24,32 @@ DeviceContext, Registered, // }, - error::to_result, + error::{ + from_err_ptr, + to_result, // + }, io::{ Io, IoCapable, IoKnownSize, // }, prelude::*, - sync::aref::ARef, + scatterlist, + sync::{ + aref::ARef, + new_mutex, + Mutex, + SetOnce, // + }, types::Opaque, // }; use core::{ ffi::c_void, marker::PhantomData, - mem::MaybeUninit, // + mem::{ + ManuallyDrop, + MaybeUninit, // + }, ops::{ Deref, DerefMut, // @@ -87,6 +104,11 @@ pub struct Object { obj: Opaque, /// Parent object that owns this object's DMA reservation object. parent_resv_obj: Option>>, + /// Devres object for unmapping any SGTable on driver-unbind. + sgt_res: ManuallyDrop>>>, + #[pin] + /// Lock for protecting initialization of `sgt_res`. + sgt_lock: Mutex<()>, #[pin] inner: T, _ctx: PhantomData, @@ -145,6 +167,8 @@ pub fn new( try_pin_init!(Self { obj <- Opaque::init_zeroed(), parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), + sgt_res: ManuallyDrop::new(SetOnce::new()), + sgt_lock <- new_mutex!(()), inner <- T::new(dev, size, args), _ctx: PhantomData::, }), @@ -189,18 +213,26 @@ extern "C" fn free_callback(obj: *mut bindings::drm_g= em_object) { // - DRM always passes a valid gem object here // - We used drm_gem_shmem_create() in our create_gem_object callb= ack, so we know that // `obj` is contained within a drm_gem_shmem_object - let this =3D unsafe { container_of!(obj, bindings::drm_gem_shmem_o= bject, base) }; - - // SAFETY: - // - We're in free_callback - so this function is safe to call. - // - We won't be using the gem resources on `this` after this call. - unsafe { bindings::drm_gem_shmem_release(this) }; + let base =3D unsafe { container_of!(obj, bindings::drm_gem_shmem_o= bject, base) }; =20 // SAFETY: // - We verified above that `obj` is valid, which makes `this` val= id // - This function is set in AllocOps, so we know that `this` is c= ontained within a // `Object` - let this =3D unsafe { container_of!(Opaque::cast_from(this), Self,= obj) }.cast_mut(); + let this =3D unsafe { container_of!(Opaque::cast_from(base), Self,= obj) }.cast_mut(); + + // We need to drop `sgt_res` first, since doing so requires that t= he GEM object is still + // alive. + // SAFETY: + // - We verified above that `this` is valid. + // - We are in free_callback, guaranteeing we have exclusive acces= s to `this` and that + // `sgt_res` will not be used after dropping it here. + unsafe { ManuallyDrop::drop(&mut (*this).sgt_res) }; + + // SAFETY: + // - We're in free_callback - so this function is safe to call. + // - We won't be using the gem resources on `this` after this call. + unsafe { bindings::drm_gem_shmem_release(base) }; =20 // SAFETY: We're recovering the Kbox<> we created in gem_create_ob= ject() let _ =3D unsafe { KBox::from_raw(this) }; @@ -279,6 +311,45 @@ pub fn vmap(&self) -> Result> { pub fn owned_vmap(&self) -> Result> { self.make_vmap() } + + /// Creates (if necessary) and returns an immutable reference to a sca= tter-gather table of DMA + /// pages for this object. + /// + /// This will pin the object in memory. It is expected that `dev` shou= ld be a pointer to the + /// same [`device::Device`] which `self` belongs to, otherwise this fu= nction will return + /// `Err(EINVAL)`. + pub fn sg_table<'a>( + &'a self, + dev: &'a device::Device, + ) -> Result<&'a scatterlist::SGTable> { + if dev.as_raw() !=3D self.dev().as_ref().as_raw() { + return Err(EINVAL); + } + + let sgt_res =3D 'out: { + // Fast path: sgt_res is already initialized + if let Some(sgt_res) =3D self.sgt_res.as_ref() { + break 'out sgt_res; + } + + // Slow path: Grab the lock and see if we need to initialize s= gt_res. + let _guard =3D self.sgt_lock.lock(); + + // If someone initialized it while we were waiting, we can exi= t early. + if let Some(sgt_res) =3D self.sgt_res.as_ref() { + break 'out sgt_res; + } + + // If not, finish initializing and return. + self.sgt_res + .populate(Devres::new(dev, SGTableMap::new(self))?); + + // SAFETY: We just populated sgt_res above. + unsafe { self.sgt_res.as_ref().unwrap_unchecked() } + }; + + Ok(sgt_res.access(dev)?) + } } =20 impl Deref for Object { @@ -474,6 +545,63 @@ impl IoKnownSize for VMap<= D, R, C, SIZE> #[cfg(CONFIG_64BIT)] impl_vmap_io_capable!(VMap, u64); =20 +/// A reference to a GEM object that is known to have a mapped [`SGTable`]. +/// +/// This is used by the Rust bindings with [`Devres`] in order to ensure t= hat mappings for SGTables +/// on GEM shmem objects are revoked on driver-unbind. +/// +/// # Invariants +/// +/// - `self.obj` always points to a valid GEM object. +/// - This object is proof that `self.obj.owner.sgt` has an initialized an= d valid +/// [`scatterlist::SGTable`]. +pub struct SGTableMap { + obj: NonNull>, +} + +impl Deref for SGTableMap { + type Target =3D scatterlist::SGTable; + + fn deref(&self) -> &Self::Target { + // SAFETY: + // - The NonNull is guaranteed to be valid via our type invariants. + // - The sgt field is guaranteed to be initialized and valid via o= ur type invariants. + unsafe { scatterlist::SGTable::from_raw((*self.obj.as_ref().as_raw= _shmem()).sgt) } + } +} + +impl Drop for SGTableMap { + fn drop(&mut self) { + // SAFETY: `obj` is always valid via our type invariants + let obj =3D unsafe { self.obj.as_ref() }; + let _lock =3D DmaResvGuard::new(obj); + + // SAFETY: We acquired the lock needed for calling this function a= bove + unsafe { bindings::__drm_gem_shmem_free_sgt_locked(obj.as_raw_shme= m()) }; + } +} + +impl SGTableMap { + fn new(obj: &Object) -> impl Init { + // INVARIANT: + // - We call drm_gem_shmem_get_pages_sgt_locked below and check wh= ether or not it + // succeeds, fulfilling the invariant of SGTableMap that the obj= ect's `sgt` field is + // initialized. + // SAFETY: + // - `obj` is fully initialized, making this function safe to call. + from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(obj.as= _raw_shmem()) })?; + + Ok(Self { obj: obj.into() }) + } +} + +// SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invar= iants, and the GEM object +// it points to is guaranteed to be thread-safe. +unsafe impl Send for SGTableMap {} +// SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invar= iants, and the GEM object +// it points to is guaranteed to be thread-safe. +unsafe impl Sync for SGTableMap {} + #[kunit_tests(rust_drm_gem_shmem)] mod tests { use super::*; @@ -582,4 +710,28 @@ fn vmap_io() -> Result { =20 Ok(()) } + + // TODO: I would love to actually test the success paths of sg_table()= , but that would require + // also implementing dummy dma_ops so that trying to create a mapping = doesn't explode. So, leave + // that for someone else. + + // Ensures that passing the wrong device to sg_table() fails as we exp= ect, and also ensure it + // skips initializing `sgt_res` since we could otherwise create `sgt_r= es` with the wrong device + // bound to it. + #[test] + fn fail_sg_table_on_wrong_dev() -> Result { + let (_dev, drm) =3D create_drm_dev()?; + let wrong_dev =3D faux::Registration::new(c"EvilKunit", None)?; + + let obj =3D Object::::new(&drm, PAGE_SIZE, ObjectC= onfig::default(), ())?; + + assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL= ); + + // If sgt_res was not initialized mistakenly with the wrong device= , this should still fail. + assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL= ); + + // TODO: Someday, we should test that creating an sg_table here st= ill succeeds. + + Ok(()) + } } --=20 2.54.0