From mboxrd@z Thu Jan 1 00:00:00 1970 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: 8bit X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 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 = MaybeUninit::uninit(); + + // SAFETY: drm_gem_shmem_vmap can be called with the DMA reservation lock held + to_result(unsafe { + // TODO: see top of file + bindings::dma_resv_lock(self.raw_dma_resv(), core::ptr::null_mut()); + let ret = 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 = self.raw_dma_resv(); + + // SAFETY: + // - This function is safe to call with the DMA reservation lock held + // - Our `ARef` is proof that the underlying gem object here is initialized 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 object. + pub fn vmap(&self) -> Result> { + let map = 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(), + }) + } } impl Deref for Object { @@ -267,6 +339,102 @@ impl driver::AllocImpl for Object { }; } +/// 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 = 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 = IoSysMapRef<'a, SIZE>; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +impl<'a, D: DriverObject, const SIZE: usize> DerefMut for VMapRef<'a, D, SIZE> { + 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 created 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 in kernel address space. +/// +/// # Invariants +/// +/// - The size of `owner` is >= 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 object 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> for VMap { + fn from(value: VMapRef<'a, D, SIZE>) -> Self { + let this = 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 gem object. + // - `size` is >= SIZE according to our type invariants, ensuring that we can never pass an + // invalid `size` to `IoSysMapRef::new(). + unsafe { + IoSysMapRef::new(self.map.clone(), self.owner.size()).unwrap_unchecked() + } + } + + /// 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 created 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 object, 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 @@ 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` bytes. /// - The caller guarantees that the mapping remains valid for the lifetime of `'a`. - #[expect(unused)] pub(crate) unsafe fn new(map: RawIoSysMap, size: usize) -> Result { if size < SIZE { return Err(EINVAL); -- 2.53.0