From nobody Mon Feb 9 01:29:16 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 82D643161AE for ; Fri, 6 Feb 2026 22:35:47 +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=1770417347; cv=none; b=XjmpB1IA1OMmZRtzVurKTKXf+KdHLIuZMh42NCg3PRwexe7PR/sHGCgQ9z+xQ20mgP4KjRJLqhMBONSbzfHAM9erGZ0Q32CvOSgNnWc9YOUMWLVYAMpupv1FTkBesQ+lAhVSEDpeWH89rpSNUTMz9ht202/M7TzmtaVNY26CniA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770417347; c=relaxed/simple; bh=RN/nEAE1lIEvZ+S6m8XaJKvBL7hfqsvNE/ElFlRmFKM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RvC+IxHKMluI4Y7Qct/WsJFDhRAda5GwTEtw4JbkthFWksfx/5ubQ1Ob3FKRAISI6olm+pQtyAYt8fpoJ+fCdqG5kbM45ZINF0ScMIUlGrglzIF1OKJwvJpG51Tgs53NEfvUXWHQGXVAu5L+UeKKGyvusOREZ2jjw40PpBCaU0Q= 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=CVTF+Czs; 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="CVTF+Czs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770417346; 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=zTebqQ2eipm8YqiSwO1TAoGzLEbFz+blNfHKTyJxzv0=; b=CVTF+CzsaWdssIDtSqNjIP9xkQahXDXWe8OaWNLup3PronQ5r9miwJclYxCUp4zdI1paL8 AdDPiHskiK3TZfUarueceTTbvg7GrWydy1AMV77rqWCV8+ENLnf9sXL/pBLSQX++b7KC2N zbj3POu14tlhhPV1nwVh474JMEHwaK0= 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-584-3KZVlp8tORq_6hbtHCtJMA-1; Fri, 06 Feb 2026 17:35:41 -0500 X-MC-Unique: 3KZVlp8tORq_6hbtHCtJMA-1 X-Mimecast-MFC-AGG-ID: 3KZVlp8tORq_6hbtHCtJMA_1770417339 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 B73581956094; Fri, 6 Feb 2026 22:35:39 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.64.226]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E264519373D8; Fri, 6 Feb 2026 22:35:37 +0000 (UTC) From: Lyude Paul To: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Cc: nouveau@lists.freedesktop.org, Daniel Almeida , Gary Guo , Benno Lossin , Alexandre Courbot , Janne Grunau Subject: [PATCH v7 7/7] rust: drm/gem: Add vmap functions to shmem bindings Date: Fri, 6 Feb 2026 17:34:31 -0500 Message-ID: <20260206223431.693765-8-lyude@redhat.com> In-Reply-To: <20260206223431.693765-1-lyude@redhat.com> References: <20260206223431.693765-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.0 on 10.30.177.17 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, specifically iosys mappings. Now that we've added iosys_map rust bindings to the kernel, let's hook these up in gem shmem. Similar to how we handle SGTables, we make sure there's two different types of mappings: owned mappings (kernel::drm::gem::shmem::VMap) and borrowed mappings (kernel::drm::gem::shmem::VMapRef). One last note: we change the #[expect(unused)] for RawIoSysMap::from_raw() to an #[allow(unused)]. Normally we would simply remove the lint assertion, however - since shmem is conditionally built, we need allow to avoid hitting warnings in certain kernel configurations. Signed-off-by: Lyude Paul --- V7: * Switch over to the new iosys map bindings that use the Io trait rust/kernel/drm/gem/shmem.rs | 170 ++++++++++++++++++++++++++++++++++- rust/kernel/iosys_map.rs | 3 +- 2 files changed, 170 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index e511a9b6710e0..604fb10325d1e 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -21,6 +21,7 @@ from_err_ptr, to_result, // }, + iosys_map::*, prelude::*, scatterlist, types::{ @@ -29,13 +30,18 @@ }, // }; use core::{ + mem::{ + self, + MaybeUninit, // + }, ops::{ Deref, DerefMut, // }, - ptr::NonNull, + ptr::NonNull, // }; use gem::{ + BaseObject, BaseObjectPrivate, DriverObject, IntoGEMObject, // @@ -216,6 +222,72 @@ pub fn owned_sg_table(&self) -> Result> { _owner: self.into(), }) } + + /// Attempt to create a [`RawIoSysMap`] from the gem object. + fn raw_vmap(&self) -> Result> { + let mut map: MaybeUninit =3D MaybeUninit::uni= nit(); + + // SAFETY: drm_gem_shmem_vmap can be called with the DMA reservati= on lock held + to_result(unsafe { + // TODO: see top of file + bindings::dma_resv_lock(self.raw_dma_resv(), core::ptr::null_m= ut()); + let ret =3D bindings::drm_gem_shmem_vmap_locked(self.as_shmem(= ), map.as_mut_ptr()); + bindings::dma_resv_unlock(self.raw_dma_resv()); + ret + })?; + + // SAFETY: if drm_gem_shmem_vmap did not fail, map is initialized = now + Ok(unsafe { RawIoSysMap::::from_raw(map.assume_init()) }) + } + + /// Unmap a [`RawIoSysMap`] from the gem object. + /// + /// # Safety + /// + /// - The caller promises that `map` came from a prior call to [`Self:= :raw_vmap`] on this gem + /// object. + /// - The caller promises that the memory pointed to by `map` will no = longer be accesed through + /// this instance. + unsafe fn raw_vunmap(&self, map: &mut RawIoSysMap) { + let resv =3D self.raw_dma_resv(); + + // 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 { + // TODO: see top of file + bindings::dma_resv_lock(resv, core::ptr::null_mut()); + bindings::drm_gem_shmem_vunmap_locked(self.as_shmem(), map.as_= raw_mut()); + bindings::dma_resv_unlock(resv); + } + } + + /// Creates and returns a virtual kernel memory mapping for this objec= t. + pub fn vmap(&self) -> Result> { + let map =3D self.raw_vmap()?; + + Ok(VMapRef { + // SAFETY: + // - The size of the vmap is the same as the size of the gem + // - The vmap will remain alive until this object is dropped. + map: unsafe { IoSysMapRef::::new(map, self.size()) }?, + owner: self, + }) + } + + /// Creates and returns an owned reference to a virtual kernel memory = mapping for this object. + pub fn owned_vmap(&self) -> Result> { + // INVARIANT: We verify here that the mapping is at least of SIZE = bytes. + if self.size() < SIZE { + return Err(EINVAL); + } + + Ok(VMap { + map: self.raw_vmap()?, + owner: self.into(), + }) + } } =20 impl Deref for Object { @@ -267,6 +339,102 @@ impl driver::AllocImpl for Object= { }; } =20 +/// A borrowed reference to a virtual mapping for a shmem-based GEM object= in kernel address space. +pub struct VMapRef<'a, D: DriverObject, const SIZE: usize =3D 0> { + map: IoSysMapRef<'a, SIZE>, + owner: &'a Object, +} + +impl<'a, D: DriverObject, const SIZE: usize> Clone for VMapRef<'a, D, SIZE= > { + fn clone(&self) -> Self { + // SAFETY: We have a successful vmap already, so this can't fail + unsafe { self.owner.vmap().unwrap_unchecked() } + } +} + +impl<'a, D: DriverObject, const SIZE: usize> Deref for VMapRef<'a, D, SIZE= > { + type Target =3D IoSysMapRef<'a, SIZE>; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +impl<'a, D: DriverObject, const SIZE: usize> DerefMut for VMapRef<'a, D, S= IZE> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.map + } +} + +impl<'a, D: DriverObject, const SIZE: usize> Drop for VMapRef<'a, D, SIZE>= { + fn drop(&mut self) { + // SAFETY: Our existence is proof that this map was previously cre= ated using self.owner. + unsafe { self.owner.raw_vunmap(&mut self.map.raw_map) }; + } +} + +/// An owned reference to a virtual mapping for a shmem-based GEM object i= n kernel address space. +/// +/// # Invariants +/// +/// - The size of `owner` is >=3D SIZE. +/// - The memory pointed to by `map` is at least as large as `T`. +/// - The memory pointed to by `map` remains valid at least until this obj= ect is dropped. +pub struct VMap { + map: RawIoSysMap, + owner: ARef>, +} + +impl Clone for VMap { + fn clone(&self) -> Self { + // SAFETY: We have a successful vmap already, so this can't fail + unsafe { self.owner.owned_vmap().unwrap_unchecked() } + } +} + +impl<'a, D: DriverObject, const SIZE: usize> From> fo= r VMap { + fn from(value: VMapRef<'a, D, SIZE>) -> Self { + let this =3D Self { + map: value.map.raw_map.clone(), + owner: value.owner.into(), + }; + + mem::forget(value); + this + } +} + +impl VMap { + /// Return a reference to the iosys map for this `VMap`. + #[inline] + pub fn get(&self) -> IoSysMapRef<'_, SIZE> { + // SAFETY: + // - The size of the iosys_map is equivalent to the size of the ge= m object. + // - `size` is >=3D SIZE according to our type invariants, ensurin= g that we can never pass an + // invalid `size` to `IoSysMapRef::new(). + unsafe { + IoSysMapRef::new(self.map.clone(), self.owner.size()).unwrap_u= nchecked() + } + } + + /// Borrows a reference to the object that owns this virtual mapping. + pub fn owner(&self) -> &Object { + &self.owner + } +} + +impl Drop for VMap { + fn drop(&mut self) { + // SAFETY: Our existence is proof that this map was previously cre= ated using self.owner + unsafe { self.owner.raw_vunmap(&mut self.map) }; + } +} + +/// SAFETY: `iosys_map` objects are safe to send across threads. +unsafe impl Send for VMap {} +/// SAFETY: `iosys_map` objects are safe to send across threads. +unsafe impl Sync for VMap {} + /// An owned reference to a scatter-gather table of DMA address spans for = a GEM shmem object. /// /// This object holds an owned reference to the underlying GEM shmem objec= t, ensuring that the diff --git a/rust/kernel/iosys_map.rs b/rust/kernel/iosys_map.rs index 2070f0fb42cb8..b649d2de83093 100644 --- a/rust/kernel/iosys_map.rs +++ b/rust/kernel/iosys_map.rs @@ -33,7 +33,7 @@ =20 impl RawIoSysMap { /// Convert from a raw `bindings::iosys_map`. - #[expect(unused)] + #[allow(unused)] #[inline] pub(crate) fn from_raw(val: bindings::iosys_map) -> Self { Self(val) @@ -139,7 +139,6 @@ impl<'a, const SIZE: usize> IoSysMapRef<'a, SIZE> { /// /// - The caller guarantees that the mapping is of at least `size` byt= es. /// - The caller guarantees that the mapping remains valid for the lif= etime of `'a`. - #[expect(unused)] pub(crate) unsafe fn new(map: RawIoSysMap, size: usize) -> Resul= t { if size < SIZE { return Err(EINVAL); --=20 2.53.0