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.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 7474437106D for ; Wed, 11 Mar 2026 19:53:44 +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=1773258827; cv=none; b=iSzkRGmx1uXTQmBRfI0bP4JEMK2rSTsm0x1NiOTWIT2zKHvtxoPDJrUA6o6apmc44ZtXNw2d/twMyK5elWTmnfhZk4DXHmiQry66sTR+0wdFvSJSZfduzHemlZo8YiehVvcgz5pYiSdfbjNTHN01e44YEqFtWTLso+6g8dB6k9U= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773258827; c=relaxed/simple; bh=Lr684luf6NmtbrXtt3EszjWm5IVnQP/L71Cvq6VgtFI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:content-type; b=QI35ywDV2hvr4hhVboB2Hf2Cq63iTPx9pI3jFWjXKEGCme/ToihZVc/s66dDOHOOGB63trGA1UMs11WjZRtnoisrDMtwC75g8Bv+33Ze2jQ6weumuATXnWcjgAoN1LDm7n6QkiiUXXmtE/8wWDu7tV2PUQpkYL9hkaRWmyAYqRY= 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=dTZwEEKa; 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="dTZwEEKa" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1773258823; 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=iZcfx1FVb+KAsQQ+WSEa4fJpAcClcy+BQx0fgVFmOUE=; b=dTZwEEKaoFula9QttgjHldGmAkIAWa1TnfznHcIUAxEFN6G7CMGWszFxEmFeMIjs51WBxY cViFP1vQMkMa16H4R/+yFdCGlesGQELPVG7kLf5QFgS09StSTBbuhB8eKY+96xvzh0FiZz wFccRrbKaniCZqv+Xe8hlMDzYdop+fE= 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-81-YaVsQSCgNGijPX6lV4VZyA-1; Wed, 11 Mar 2026 15:53:40 -0400 X-MC-Unique: YaVsQSCgNGijPX6lV4VZyA-1 X-Mimecast-MFC-AGG-ID: YaVsQSCgNGijPX6lV4VZyA_1773258817 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 CA5A519560B3; Wed, 11 Mar 2026 19:53:36 +0000 (UTC) Received: from GoldenWind.redhat.com (unknown [10.22.81.64]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 49EEE30002D2; Wed, 11 Mar 2026 19:53:32 +0000 (UTC) From: Lyude Paul To: nouveau@lists.freedesktop.org, Gary Guo , Daniel Almeida , rust-for-linux@vger.kernel.org Cc: Matthew Maurer , FUJITA Tomonori , Lorenzo Stoakes , christian.koenig@amd.com, Asahi Lina , Miguel Ojeda , Andreas Hindborg , Simona Vetter , Alice Ryhl , Boqun Feng , Sumit Semwal , Krishna Ketan Rai , linux-media@vger.kernel.org, Shankari Anand , David Airlie , Benno Lossin , Viresh Kumar , linaro-mm-sig@lists.linaro.org, Asahi Lina , Greg Kroah-Hartman , kernel@vger.kernel.org Subject: [PATCH v8 7/7] rust: drm/gem: Add vmap functions to shmem bindings Date: Wed, 11 Mar 2026 15:52:46 -0400 Message-ID: <20260311195246.2439593-8-lyude@redhat.com> In-Reply-To: <20260311195246.2439593-1-lyude@redhat.com> References: <20260311195246.2439593-1-lyude@redhat.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-MFC-PROC-ID: gwE0_ZCyW_sEW12yUrAQefvlTSmcEafFjCV6GVAzmAI_1773258817 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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. 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). Signed-off-by: Lyude Paul --- 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() rust/kernel/drm/gem/shmem.rs | 231 ++++++++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 1 deletion(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 3fab5d76c197b..33f46fee87332 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -21,6 +21,11 @@ from_err_ptr, to_result, // }, + io::{ + Io, + IoCapable, + IoKnownSize, // + }, prelude::*, scatterlist, types::{ @@ -29,13 +34,22 @@ }, // }; use core::{ + ffi::c_void, + mem::{ + self, + MaybeUninit, // + }, ops::{ Deref, DerefMut, // }, - ptr::NonNull, + ptr::{ + self, + NonNull, // + }, }; use gem::{ + BaseObject, BaseObjectPrivate, DriverObject, IntoGEMObject, // @@ -217,6 +231,82 @@ pub fn owned_sg_table(&self) -> Result> { _owner: self.into(), }) } + + /// Attempt to create a [`RawIoSysMap`] from the gem object. + fn raw_vmap(&self) -> Result<*mut c_void> { + 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(), ptr::null_mut()); + let ret = bindings::drm_gem_shmem_vmap_locked(self.as_raw_shmem(), map.as_mut_ptr()); + bindings::dma_resv_unlock(self.raw_dma_resv()); + ret + })?; + + // SAFETY: The call to drm_gem_shmem_vunmap_locked succeeded above, so we are guaranteed + // that map is properly initialized. + let map = unsafe { map.assume_init() }; + + // XXX: We don't currently support iomem allocations + if map.is_iomem { + // SAFETY: + // - The vmap operation above succeeded, making it safe to call vunmap + // - We checked that this is an iomem allocation, making it safe to read vaddr_iomem + unsafe { self.raw_vunmap(map.__bindgen_anon_1.vaddr_iomem) }; + + Err(ENOTSUPP) + } else { + // SAFETY: We checked that this is not an iomem allocation, making it safe to read vaddr + Ok(unsafe { map.__bindgen_anon_1.vaddr }) + } + } + + /// Unmap a [`RawIoSysMap`] from the gem object. + /// + /// # Safety + /// + /// - The caller promises that addr came from a prior call to [`Self::raw_vmap`] on this gem + /// object. + /// - The caller promises that the memory pointed to by addr will no longer be accesed through + /// this instance. + unsafe fn raw_vunmap(&self, vaddr: *mut c_void) { + let resv = self.raw_dma_resv(); + let mut map = bindings::iosys_map { + is_iomem: false, + __bindgen_anon_1: bindings::iosys_map__bindgen_ty_1 { vaddr }, + }; + + // 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, ptr::null_mut()); + bindings::drm_gem_shmem_vunmap_locked(self.as_raw_shmem(), &mut map); + bindings::dma_resv_unlock(resv); + } + } + + /// Creates and returns a virtual kernel memory mapping for this object. + #[inline] + pub fn vmap(&self) -> Result> { + Ok(VMapRef { + addr: self.raw_vmap()?, + owner: self, + }) + } + + /// Creates and returns an owned reference to a virtual kernel memory mapping for this object. + #[inline] + pub fn owned_vmap(&self) -> Result> { + Ok(VMap { + addr: self.raw_vmap()?, + owner: self.into(), + }) + } } impl Deref for Object { @@ -268,6 +358,145 @@ impl driver::AllocImpl for Object { }; } +macro_rules! impl_vmap_io_capable { + ($impl:ident, $ty:ty $(, $lifetime:lifetime )?) => { + impl<$( $lifetime ,)? D: DriverObject, const SIZE: usize> IoCapable<$ty> + for $impl<$( $lifetime ,)? D, SIZE> + { + #[inline(always)] + unsafe fn io_read(&self, address: usize) -> $ty { + let ptr = address as *mut $ty; + + // SAFETY: The safety contract of `io_read` guarantees that address is a valid + // address within the bounds of `Self` of at least the size of $ty, and is properly + // aligned. + unsafe { ptr::read(ptr) } + } + + #[inline(always)] + unsafe fn io_write(&self, value: $ty, address: usize) { + let ptr = address as *mut $ty; + + // SAFETY: The safety contract of `io_write` guarantees that address is a valid + // address within the bounds of `Self` of at least the size of $ty, and is properly + // aligned. + unsafe { ptr::write(ptr, value) } + } + } + }; +} + +// Implement various traits common to both VMap types +macro_rules! impl_vmap_common { + ($impl:ident $(, $lifetime:lifetime )?) => { + impl<$( $lifetime ,)? D, const SIZE: usize> $impl<$( $lifetime ,)? D, SIZE> + where + D: DriverObject, + { + /// Borrows a reference to the object that owns this virtual mapping. + #[inline(always)] + pub fn owner(&self) -> &Object { + &self.owner + } + } + + impl<$( $lifetime ,)? D, const SIZE: usize> Drop for $impl<$( $lifetime ,)? D, SIZE> + where + D: DriverObject, + { + #[inline(always)] + fn drop(&mut self) { + // SAFETY: Our existence is proof that this map was previously created using + // self.owner + unsafe { self.owner.raw_vunmap(self.addr) }; + } + } + + impl<$( $lifetime ,)? D, const SIZE: usize> Io for $impl<$( $lifetime ,)? D, SIZE> + where + D: DriverObject, + { + #[inline(always)] + fn addr(&self) -> usize { + self.addr as usize + } + + #[inline(always)] + fn maxsize(&self) -> usize { + self.owner.size() + } + } + + impl<$( $lifetime ,)? D, const SIZE: usize> IoKnownSize for $impl<$( $lifetime ,)? D, SIZE> + where + D: DriverObject, + { + const MIN_SIZE: usize = SIZE; + } + + impl_vmap_io_capable!($impl, u8 $( , $lifetime )?); + impl_vmap_io_capable!($impl, u16 $( , $lifetime )?); + impl_vmap_io_capable!($impl, u32 $( , $lifetime )?); + #[cfg(CONFIG_64BIT)] + impl_vmap_io_capable!($impl, u64 $( , $lifetime )?); + }; +} + +/// 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 addr is at least as large as `T`. +/// - The memory pointed to by addr remains valid at least until this object is dropped. +pub struct VMap { + addr: *mut c_void, + owner: ARef>, +} + +impl_vmap_common!(VMap); + +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 { + addr: value.addr, + owner: value.owner.into(), + }; + + mem::forget(value); + this + } +} + +// SAFETY: addr is guaranteed to be valid and accessible for the lifetime of VMap, ensuring its +// safe to send across threads. +unsafe impl Send for VMap {} +// SAFETY: addr is guaranteed to be valid and accessible for the lifetime of VMap, ensuring its +// safe to send across threads. +unsafe impl Sync for VMap {} + +/// 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> { + addr: *mut c_void, + owner: &'a Object, +} + +impl_vmap_common!(VMapRef, 'a); + +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() } + } +} + /// 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 -- 2.53.0