From nobody Sat Feb 7 18:21:14 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 0E5B11DB375 for ; Thu, 23 Oct 2025 21:26:06 +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=1761254770; cv=none; b=F0EUhsd2Py4x9BImf8IbfrP0XXtS5nIDhn8nj6T0OkaCqxYRn1GuBk5E3mlWFpUItSw61XGLpzrGFX50iX3pyNnCdDyM7E3X2sav5SJoYYBZ5rBJ4hp12zNpUzPiPnyZngtf/NlHa2mAtH9OPS9TV+KWvEuejOU0uZxxP2LhZxA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254770; c=relaxed/simple; bh=lwDclK5zyxp1awpvPaJAKW/PWA9pPm0C7jX9Z+AxEaU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UPxsmkryh4M6b+DoZkFf0d13ZTG0L8GLUJUuqgEYVS7nuw7XL81sQKrOxpZYRmaKVn1r1JT9hQ/rfnmnpKhcnzz1G+WZMYVLHBu5x/BOYprmdaOBBBcVjQl8InDOESfQyA32NPPCwWjHK1W05QG7A+sDJJdQeXceAUXA02LV5NU= 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=d5JtqhXR; 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="d5JtqhXR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254766; 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=tojreo2SCeXW/AFAiQR1a/N+EZ+cx5QocDZTELD04Hw=; b=d5JtqhXRcdiO0EwQXjzRGZU3Vc4mvpeXz75Q5ySlP/2V+w48JHRR7CaPRkvMBQy+3OYBNI WPIt1GXey3v5ACU6XXlUJfE3+9/o4jc6h12HTCuQkcauBxxgZnw4/xRZG/LAt0nDvDp2Iy G2rwpr0phC7mcOzJW4gYVrXHu8vGAac= 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-504-bgxhGK6HM7qRBtyz1apc3Q-1; Thu, 23 Oct 2025 17:26:03 -0400 X-MC-Unique: bgxhGK6HM7qRBtyz1apc3Q-1 X-Mimecast-MFC-AGG-ID: bgxhGK6HM7qRBtyz1apc3Q_1761254761 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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 14FFF1800D89; Thu, 23 Oct 2025 21:26:01 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5975B30002E1; Thu, 23 Oct 2025 21:25:56 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: David Airlie , Simona Vetter , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Asahi Lina , Shankari Anand Subject: [PATCH v5 1/8] rust/drm: Add gem::impl_aref_for_gem_obj! Date: Thu, 23 Oct 2025 17:22:03 -0400 Message-ID: <20251023212540.1141999-2-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 Content-Type: text/plain; charset="utf-8" In the future we're going to be introducing more GEM object types in rust then just gem::Object. Since all types of GEM objects have refcounting, let's introduce a macro that we can use in the gem crate in order to copy this boilerplate implementation for each type: impl_aref_for_gem_obj!(). Signed-off-by: Lyude Paul Reviewed-by: Daniel Almeida --- V5: * Move .as_raw() call to `let obj` in dec_ref, to ensure that the reference to object is not live by the time that we call drm_gem_object_put(). * Add missing #[macro_export] annotation rust/kernel/drm/gem/mod.rs | 54 +++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 20c2769a8c9d6..32bff2e8463f4 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -15,6 +15,44 @@ }; use core::{ops::Deref, ptr::NonNull}; =20 +/// A macro for implementing [`AlwaysRefCounted`] for any GEM object type. +/// +/// Since all GEM objects use the same refcounting scheme. +#[macro_export] +macro_rules! impl_aref_for_gem_obj { + ( + impl $( <$( $tparam_id:ident ),+> )? for $type:ty + $( + where + $( $bind_param:path : $bind_trait:path ),+ + )? + ) =3D> { + // SAFETY: All gem objects are refcounted + unsafe impl $( <$( $tparam_id ),+> )? $crate::types::AlwaysRefCoun= ted for $type + $( + where + $( $bind_param : $bind_trait ),+ + )? + { + 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()) }; + } + + unsafe fn dec_ref(obj: core::ptr::NonNull) { + // SAFETY: `obj` is a valid pointer to an `Object`. + let obj =3D unsafe { obj.as_ref() }.as_raw(); + + // SAFETY: The safety requirements guarantee that the refc= ount is non-zero. + unsafe { bindings::drm_gem_object_put(obj) }; + } + } + }; +} + +pub(crate) use impl_aref_for_gem_obj; + /// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementatio= n from its /// [`DriverObject`] implementation. /// @@ -253,21 +291,7 @@ extern "C" fn free_callback(obj: *mut bindings::drm_ge= m_object) { } } =20 -// SAFETY: Instances of `Object` are always reference-counted. -unsafe impl crate::types::AlwaysRefCounted for Object { - 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()) }; - } - - unsafe fn dec_ref(obj: NonNull) { - // SAFETY: `obj` is a valid pointer to an `Object`. - let obj =3D unsafe { obj.as_ref() }; - - // SAFETY: The safety requirements guarantee that the refcount is = non-zero. - unsafe { bindings::drm_gem_object_put(obj.as_raw()) } - } -} +impl_aref_for_gem_obj!(impl for Object where T: DriverObject); =20 impl super::private::Sealed for Object {} =20 --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 9185429DB61 for ; Thu, 23 Oct 2025 21:26:17 +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=1761254779; cv=none; b=fKSpJ0kaRjrXHoWWiPdow+9vdptGILtWMwBjx1Komkj0U3E94Ciu3Ah3vR+lKYb6ox2lEloeNvZ9MZ7GOWarMAUe9Fp//ye0iPA23PczkcLDiChtG6+PkABjCjjYRG6+VH36NJVaUwYEEMF2xTVGQJDUetLTe7yBBwZ5hddAxSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254779; c=relaxed/simple; bh=8IH6u17Bf6UMu+UJoSDGO6p0qZjeVFKihLao9s658Po=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DuIp8WYRBUjl1KYkwIqtB2ktLw4S/Bd//yZ5zzmBuRK2XzH728NykLN4sb9k3JsIK5EPP1B2Hj9JrT4X4/zLZNaRNNa1UV/azv3EE7pYFp1Z2ZZ4OZFQuTAw6QveajMrf7ERSzY8mxmI8/Edgx5pIb8u1dAbSyHYpQQUjoHmKbc= 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=JsoZVmgB; 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="JsoZVmgB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254776; 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=YJB3O0IxSeB2TEy9+wLYaow+jm2Lp5gpdA5YwObW84Q=; b=JsoZVmgBkUHf1q2FDwJk9c/fiDLSOXjWZy4lQIKCXl7VCi/aGRidARDfKHVNjmLmaOJ1nV DEA1g810zbWzSk7fShtT+qxumm2mGZXYTr1GnkCs/IMo9a88zKqVnrLXkv3wWsg9NA1JPL fMYMyNOdh5hKKE08ZGnahKARAFAdI/A= 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-395-qtjIiVRXNGCD4PYf4KBpOQ-1; Thu, 23 Oct 2025 17:26:12 -0400 X-MC-Unique: qtjIiVRXNGCD4PYf4KBpOQ-1 X-Mimecast-MFC-AGG-ID: qtjIiVRXNGCD4PYf4KBpOQ_1761254769 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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id F3ADF1956089; Thu, 23 Oct 2025 21:26:08 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7E63C30002E1; Thu, 23 Oct 2025 21:26:03 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: Asahi Lina , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Greg Kroah-Hartman , Asahi Lina , Viresh Kumar , FUJITA Tomonori , Krishna Ketan Rai , linux-media@vger.kernel.org (open list:DMA BUFFER SHARING FRAMEWORK:Keyword:\bdma_(?:buf|fence|resv)\b), linaro-mm-sig@lists.linaro.org (moderated list:DMA BUFFER SHARING FRAMEWORK:Keyword:\bdma_(?:buf|fence|resv)\b) Subject: [PATCH v5 2/8] rust: helpers: Add bindings/wrappers for dma_resv_lock Date: Thu, 23 Oct 2025 17:22:04 -0400 Message-ID: <20251023212540.1141999-3-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 Content-Type: text/plain; charset="utf-8" From: Asahi Lina This is just for basic usage in the DRM shmem abstractions for implied locking, not intended as a full DMA Reservation abstraction yet. Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 1 + rust/helpers/dma-resv.c | 13 +++++++++++++ rust/helpers/helpers.c | 1 + 3 files changed, 15 insertions(+) create mode 100644 rust/helpers/dma-resv.c diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 2e43c66635a2c..07f79e125c329 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/dma-resv.c b/rust/helpers/dma-resv.c new file mode 100644 index 0000000000000..05501cb814513 --- /dev/null +++ b/rust/helpers/dma-resv.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx = *ctx) +{ + return dma_resv_lock(obj, ctx); +} + +void rust_helper_dma_resv_unlock(struct dma_resv *obj) +{ + dma_resv_unlock(obj); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 551da6c9b5064..36d40f911345c 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -25,6 +25,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma-resv.c" #include "drm.c" #include "err.c" #include "irq.c" --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 813F1292B2E for ; Thu, 23 Oct 2025 21:26:24 +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=1761254786; cv=none; b=VXd/l1NhNICldd7VS2g4JBWou6q+2/F0EskPxuhS+R/kRQNYyf41bnqKWQ9y2j2IdJI5DEZ/GZ/nTbwHf01ve/ijSrJoZerS70kKTXpSLi4oPToPDggMyoLW5DWpU0bkBBnHpkHG61nYydFchSv0kO866GaRlGRDIj7oowIb4iM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254786; c=relaxed/simple; bh=VqxoTE/YG4alKlsCQm0qmglo0PmWMRVx2BhkMcpqMrE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nlmfwafA4H9OjNgFwfpNc0M7NHXjD2LGibhofD/jlVlZxjgElLlUBC9cqhYN1TXjFEpDw41VBDpPDT2ybXi3DNfUkSxgGqCgfCaYY0aqz4trN21jrYISIhJKMG3JTqWuK/l9jBSKcvGokjF8BlQH7f7nZRsMcnUuo6pjv51EyYI= 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=NhvLM3We; 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="NhvLM3We" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254783; 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=qfraVObzTsQp66zjzlSBba4zEC8R0p7WTc8r0hvFWO8=; b=NhvLM3WeTBHZTD24SB/+ON+qcRAgWguEMZoET7e8cyrBIJQAaZ01+2CISgpbIVwjyEeqiB B0gweDRj3FrNKS5mhyKMeIJKEa6+u+qQdOsRgIbIZ4cIoVuZcMZHHu2lcAFdVKrYiM0EoR o0XqcMUTNhYDjNzTqJeTGhJyqMftALo= 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-313-2f0XLN9lO7aoKLT1ALMJ7A-1; Thu, 23 Oct 2025 17:26:20 -0400 X-MC-Unique: 2f0XLN9lO7aoKLT1ALMJ7A-1 X-Mimecast-MFC-AGG-ID: 2f0XLN9lO7aoKLT1ALMJ7A_1761254777 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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 556ED18001E3; Thu, 23 Oct 2025 21:26:17 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1CF1530002DB; Thu, 23 Oct 2025 21:26:11 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: David Airlie , Simona Vetter , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Asahi Lina , Shankari Anand , linux-media@vger.kernel.org (open list:DMA BUFFER SHARING FRAMEWORK:Keyword:\bdma_(?:buf|fence|resv)\b), linaro-mm-sig@lists.linaro.org (moderated list:DMA BUFFER SHARING FRAMEWORK:Keyword:\bdma_(?:buf|fence|resv)\b) Subject: [PATCH v5 3/8] rust: drm: gem: Add raw_dma_resv() function Date: Thu, 23 Oct 2025 17:22:05 -0400 Message-ID: <20251023212540.1141999-4-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 Content-Type: text/plain; charset="utf-8" For retrieving a pointer to the struct dma_resv for a given GEM object. We also introduce it in a new trait, BaseObjectPrivate, which we automatically implement for all gem objects and don't expose to users outside of the crate. Signed-off-by: Lyude Paul --- rust/kernel/drm/gem/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 32bff2e8463f4..67813cfb0db42 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -200,6 +200,18 @@ fn create_mmap_offset(&self) -> Result { =20 impl BaseObject for T {} =20 +/// Crate-private base operations shared by all GEM object classes. +#[expect(unused)] +pub(crate) trait BaseObjectPrivate: IntoGEMObject { + /// Return a pointer to this object's dma_resv. + fn raw_dma_resv(&self) -> *mut bindings::dma_resv { + // SAFETY: `as_gem_obj()` always returns a valid pointer to the ba= se DRM gem object + unsafe { (*self.as_raw()).resv } + } +} + +impl BaseObjectPrivate for T {} + /// A base GEM object. /// /// Invariants --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 A8835292B2E for ; Thu, 23 Oct 2025 21:26:30 +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=1761254792; cv=none; b=fdaTVOFtymMs7EAYgZKtocb5JQmUcsCJOjypDYUiX03hu4/KwQ3k5FKib3gfciXsbpa3cs5rvKzVZIgTcsAw/P3XogbMCPVuqfgvSV5Yh0yk/SRH2mwYP+DpjCpfZMxljv66AmYl9rhtQmrMHbMgZeGrWzY5Zul+dNT9x36QyDg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254792; c=relaxed/simple; bh=3TV+OPhKNYm8JAI7/3/zYXvkQdnZC44wHnaKEf17WwA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LJHMUILZT9+gUr83e8kjbajaQhU0PgatW8PdhWTkcxLg26F13mjv740REdp7ivS4wzjT98V4OWkhOgIHajRwltR8Vh9ECxICn/GuPjEFAXUrs7ilSpuXQPRlFaPTVQIc+q9KcSIw8fmm36B44PDYvrJaiv1qJ3JEaqzjwxjPsI0= 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=PWfj1XY2; 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="PWfj1XY2" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254789; 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=M8362CJTazJcHQoi+7kcx7uVRGkAE0Hkf1B4q+QXnus=; b=PWfj1XY2ztP1Or+ba9NBkXb4WCVnC2ArkXYbb70BWUD3SA49Z225sXA1ZWETA+kwGu1u8t cHU/vKrvGutdEMTWe3VXQkkluZrmndPi1eoFV/7BR0aZLF88BBtI2OGtkNM/PxBnkQOiAc C+6RQPE3vg4tui/1BMEM86XI0n/mdYk= 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-613-flIUhbKhMhat7l0uuzzOIg-1; Thu, 23 Oct 2025 17:26:26 -0400 X-MC-Unique: flIUhbKhMhat7l0uuzzOIg-1 X-Mimecast-MFC-AGG-ID: flIUhbKhMhat7l0uuzzOIg_1761254784 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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 199B518002C1; Thu, 23 Oct 2025 21:26:24 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3991030002DB; Thu, 23 Oct 2025 21:26:20 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: David Airlie , Simona Vetter , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Asahi Lina , Shankari Anand , nouveau@lists.freedesktop.org (open list:DRM DRIVER FOR NVIDIA GPUS [RUST]) Subject: [PATCH v5 4/8] rust: gem: Introduce DriverObject::Args Date: Thu, 23 Oct 2025 17:22:06 -0400 Message-ID: <20251023212540.1141999-5-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 Content-Type: text/plain; charset="utf-8" This is an associated type that may be used in order to specify a data-type to pass to gem objects when construction them, allowing for drivers to more easily initialize their private-data for gem objects. Signed-off-by: Lyude Paul Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida --- V3: * s/BaseDriverObject/DriverObject/ V4: * Fix leftover reference to BaseObjectDriver in rustdoc for DriverObject::Args drivers/gpu/drm/nova/gem.rs | 5 +++-- rust/kernel/drm/gem/mod.rs | 13 ++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs index 2760ba4f3450b..173077eeb2def 100644 --- a/drivers/gpu/drm/nova/gem.rs +++ b/drivers/gpu/drm/nova/gem.rs @@ -18,8 +18,9 @@ pub(crate) struct NovaObject {} =20 impl gem::DriverObject for NovaObject { type Driver =3D NovaDriver; + type Args =3D (); =20 - fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit { + fn new(_dev: &NovaDevice, _size: usize, _args: Self::Args) -> impl Pin= Init { try_pin_init!(NovaObject {}) } } @@ -33,7 +34,7 @@ pub(crate) fn new(dev: &NovaDevice, size: usize) -> Resul= t, size: usize) -> impl PinInit; + fn new( + dev: &drm::Device, + size: usize, + args: Self::Args, + ) -> impl PinInit; =20 /// Open a new handle to an existing object, associated with a File. fn open(_obj: &::Object, _file: &DriverFi= le) -> Result { @@ -247,11 +254,11 @@ 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, args: T::Args) -= > 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), + data <- T::new(dev, size, args), // INVARIANT: The drm subsystem guarantees that the `struc= t drm_device` will live // as long as the GEM object lives. dev: dev.into(), --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 3400D2BDC1B for ; Thu, 23 Oct 2025 21:26:53 +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=1761254816; cv=none; b=M2XXqu1twT9REnSh9Z5bNKHx2OWZ1kxOkIocL7oj+ORfBFHO9Bie+P8bnClRW2TNVXOPxHw/E5Q6ENHhVmtlfTKTgtf7ARcynndbIcKc++W74GXMzgUapxaKUeRKZKc3mUMD7/KhevkyvL+nBGE+dRF13/KQesB7f/fo4swLvB8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254816; c=relaxed/simple; bh=qLGi/Z6d1zwCb/bMpdctCfPFSQvKXPVpTF3hqjm+90E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=hc+66iaoTTDe1Ft0P1SobR43uN0q+QPqGJZP9NZ4w3yamfamkeVOLzSVMWDtAR4zFekrhsQgSevUs8fiPuiqAB5yLNhgCTt5Vh87u9/laFMZqeHPIOZArwE6X3IMivrjihRHe9OnPGKWK5NsIiZM4fQ+vhCYWZw7ivyZDdasSEs= 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=aNeIoT5I; 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="aNeIoT5I" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254813; 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=3ts9Csbs7b42C8wr8CbYr4Bh1AkEJ8zX9PxfkOV2CcE=; b=aNeIoT5Ip/0nME3AcYG64b+vrYu5J2ZKv6ZERUh4TZW0Xhxphotu/35DWUjYMRKwIIyHRC 3qg3wAYHq8Q9/l9IaCLR/H+QVCnwxm8uO9NhTLMBcOyw3d5QAURYOWoWPrpNIg2BkX08IZ 5eI3aBu3t9czNj4hbBG3asrSuhpJFeU= 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-47-T5Ui3wGrPDS6_WKq_KF7BA-1; Thu, 23 Oct 2025 17:26:45 -0400 X-MC-Unique: T5Ui3wGrPDS6_WKq_KF7BA-1 X-Mimecast-MFC-AGG-ID: T5Ui3wGrPDS6_WKq_KF7BA_1761254800 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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1D58718529A2; Thu, 23 Oct 2025 21:26:36 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 94A7930002EC; Thu, 23 Oct 2025 21:26:31 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: Asahi Lina , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , David Airlie , Simona Vetter , Greg Kroah-Hartman , Asahi Lina , Viresh Kumar , Tamir Duberstein , Shankari Anand Subject: [PATCH v5 5/8] rust: drm: gem: shmem: Add DRM shmem helper abstraction Date: Thu, 23 Oct 2025 17:22:07 -0400 Message-ID: <20251023212540.1141999-6-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 From: Asahi Lina The DRM shmem helper includes common code useful for drivers which allocate GEM objects as anonymous shmem. Add a Rust abstraction for this. Drivers can choose the raw GEM implementation or the shmem layer, depending on their needs. Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Signed-off-by: Lyude Paul --- V2: * Use the drm_gem_shmem_init() and drm_gem_shmem_release() that I extracted so we can handle memory allocation in rust, which means we no longer have to handle freeing rust members of the struct by hand and have a closer implementation to the main gem object (this also gets rid of gem_create_object) * Get rid of GemObjectRef and UniqueGemObjectRef, we have ARef at home. * Use Device in Object * Cleanup Object::::new() a bit: * Cleanup safety comment * Use cast_mut() * Just import container_of!(), we use it all over anyhow * mut_shmem() -> as_shmem(), make it safe (there's no reason for being unsa= fe) * Remove any *const and *muts in structs, just use NonNull * Get rid of the previously hand-rolled sg_table bindings in shmem, use the bindings from Abdiel's sg_table patch series * Add a TODO at the top about DMA reservation APIs and a desire for WwMutex * Get rid of map_wc() and replace it with a new ObjectConfig struct. While it currently only specifies the map_wc flag, the idea here is that settings like map_wc() and parent_resv_obj() shouldn't be exposed as normal functions since the only place where it's safe to set them is when we're still guaranteed unique access to the GEM object, e.g. before returning it to the caller. Using a struct instead of individual arguments here is mainly because we'll be adding at least one more argument, and there's enough other gem shmem settings that trying to add all of them as individual function arguments in the future would be a bit messy. * Get rid of vm_numa_fields!, Lina didn't like this macro much either and I think that it's fine for us to just specify the #[cfg(=E2=80=A6)] attribu= tes by hand since we only need to do it twice. * Set drm_gem_object_funcs.vm_ops directly to drm_gem_shmem_vm_ops, don't export the various shmem funcs. I'm not sure why this wasn't possible before but it seems to work fine now. V4: * Switch from OpaqueObject to a normal Object now that we've removed it * Remove `dev` from Object, it's redundant as drm_gem_object already has a device pointer we can use. * Use &raw instead of addr_of!() V5: * Fix typo in shmem::Object::as_raw() * Add type invariant around `obj` always being a valid `drm_gem_shmem_object` for the duration of the lifetime of `Object`. * Use Opaque::cast_from() instead of unrestricted casts rust/bindings/bindings_helper.h | 2 + rust/helpers/drm.c | 48 ++++++- rust/kernel/drm/gem/mod.rs | 3 +- rust/kernel/drm/gem/shmem.rs | 225 ++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/drm/gem/shmem.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 07f79e125c329..ad644f3d62cc3 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/drm.c b/rust/helpers/drm.c index 450b406c6f273..a4e997d0b4732 100644 --- a/rust/helpers/drm.c +++ b/rust/helpers/drm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 =20 #include +#include #include =20 #ifdef CONFIG_DRM @@ -20,4 +21,49 @@ __u64 rust_helper_drm_vma_node_offset_addr(struct drm_vm= a_offset_node *node) return drm_vma_node_offset_addr(node); } =20 -#endif +#ifdef CONFIG_DRM_GEM_SHMEM_HELPER +void rust_helper_drm_gem_shmem_object_free(struct drm_gem_object *obj) +{ + return drm_gem_shmem_object_free(obj); +} + +void rust_helper_drm_gem_shmem_object_print_info(struct drm_printer *p, un= signed int indent, + const struct drm_gem_obj= ect *obj) +{ + drm_gem_shmem_object_print_info(p, indent, obj); +} + +int rust_helper_drm_gem_shmem_object_pin(struct drm_gem_object *obj) +{ + return drm_gem_shmem_object_pin(obj); +} + +void rust_helper_drm_gem_shmem_object_unpin(struct drm_gem_object *obj) +{ + drm_gem_shmem_object_unpin(obj); +} + +struct sg_table *rust_helper_drm_gem_shmem_object_get_sg_table(struct drm_= gem_object *obj) +{ + return drm_gem_shmem_object_get_sg_table(obj); +} + +int rust_helper_drm_gem_shmem_object_vmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + return drm_gem_shmem_object_vmap(obj, map); +} + +void rust_helper_drm_gem_shmem_object_vunmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + drm_gem_shmem_object_vunmap(obj, map); +} + +int rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, stru= ct vm_area_struct *vma) +{ + return drm_gem_shmem_object_mmap(obj, vma); +} + +#endif /* CONFIG_DRM_GEM_SHMEM_HELPER */ +#endif /* CONFIG_DRM */ diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index d448c65fe5e13..2985dfa144027 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -3,6 +3,8 @@ //! DRM GEM API //! //! C header: [`include/drm/drm_gem.h`](srctree/include/drm/drm_gem.h) +#[cfg(CONFIG_DRM_GEM_SHMEM_HELPER =3D "y")] +pub mod shmem; =20 use crate::{ alloc::flags::*, @@ -208,7 +210,6 @@ fn create_mmap_offset(&self) -> Result { impl BaseObject for T {} =20 /// Crate-private base operations shared by all GEM object classes. -#[expect(unused)] pub(crate) trait BaseObjectPrivate: IntoGEMObject { /// Return a pointer to this object's dma_resv. fn raw_dma_resv(&self) -> *mut bindings::dma_resv { diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs new file mode 100644 index 0000000000000..45b95d60a3ec7 --- /dev/null +++ b/rust/kernel/drm/gem/shmem.rs @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! DRM GEM shmem helper objects +//! +//! C header: [`include/linux/drm/drm_gem_shmem_helper.h`](srctree/include= /linux/drm/drm_gem_shmem_helper.h) + +// TODO: +// - There are a number of spots here that manually acquire/release the DM= A reservation lock using +// dma_resv_(un)lock(). In the future we should add support for ww mutex= , expose a method to +// acquire a reference to the WwMutex, and then use that directly instea= d of the C functions here. + +use crate::{ + container_of, + drm::{device, driver, gem, private::Sealed}, + error::{from_err_ptr, to_result}, + prelude::*, + scatterlist, + types::{ARef, Opaque}, +}; +use core::{ + ops::{Deref, DerefMut}, + ptr::NonNull, +}; +use gem::{BaseObjectPrivate, DriverObject, IntoGEMObject}; + +/// A struct for controlling the creation of shmem-backed GEM objects. +/// +/// This is used with [`Object::new()`] to control various properties that= can only be set when +/// initially creating a shmem-backed GEM object. +#[derive(Default)] +pub struct ObjectConfig<'a, T: DriverObject> { + /// Whether to set the write-combine map flag. + pub map_wc: bool, + + /// Reuse the DMA reservation from another GEM object. + /// + /// The newly created [`Object`] will hold an owned refcount to `paren= t_resv_obj` if specified. + pub parent_resv_obj: Option<&'a Object>, +} + +/// A shmem-backed GEM object. +/// +/// # Invariants +/// +/// `obj` contains a valid initialized `struct drm_gem_shmem_object` for t= he lifetime of this +/// object. +#[repr(C)] +#[pin_data] +pub struct Object { + #[pin] + obj: Opaque, + // Parent object that owns this object's DMA reservation object + parent_resv_obj: Option>>, + #[pin] + inner: T, +} + +super::impl_aref_for_gem_obj!(impl for Object where T: DriverObject); + +impl Object { + /// `drm_gem_object_funcs` vtable suitable for GEM shmem objects. + const VTABLE: bindings::drm_gem_object_funcs =3D bindings::drm_gem_obj= ect_funcs { + free: Some(Self::free_callback), + open: Some(super::open_callback::), + close: Some(super::close_callback::), + print_info: Some(bindings::drm_gem_shmem_object_print_info), + export: None, + pin: Some(bindings::drm_gem_shmem_object_pin), + unpin: Some(bindings::drm_gem_shmem_object_unpin), + get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table), + vmap: Some(bindings::drm_gem_shmem_object_vmap), + vunmap: Some(bindings::drm_gem_shmem_object_vunmap), + mmap: Some(bindings::drm_gem_shmem_object_mmap), + status: None, + rss: None, + // SAFETY: `drm_gem_shmem_vm_ops` is static const on the C side, s= o immutable references are + // safe here and such references shall be valid forever + vm_ops: unsafe { &bindings::drm_gem_shmem_vm_ops }, + evict: None, + }; + + /// Return a raw pointer to the embedded drm_gem_shmem_object. + fn as_shmem(&self) -> *mut bindings::drm_gem_shmem_object { + self.obj.get() + } + + /// Create a new shmem-backed DRM object of the given size. + /// + /// Additional config options can be specified using `config`. + pub fn new( + dev: &device::Device, + size: usize, + config: ObjectConfig<'_, T>, + args: T::Args, + ) -> Result> { + let new: Pin> =3D KBox::try_pin_init( + try_pin_init!(Self { + obj <- Opaque::init_zeroed(), + parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), + inner <- T::new(dev, size, args), + }), + GFP_KERNEL, + )?; + + // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initial= ization above. + unsafe { (*new.as_raw()).funcs =3D &Self::VTABLE }; + + // SAFETY: The arguments are all valid via the type invariants. + to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.= as_shmem(), size) })?; + + // SAFETY: We never move out of `self`. + let new =3D KBox::into_raw(unsafe { Pin::into_inner_unchecked(new)= }); + + // SAFETY: We're taking over the owned refcount from `drm_gem_shme= m_init`. + let obj =3D unsafe { ARef::from_raw(NonNull::new_unchecked(new)) }; + + // Start filling out values from `config` + if let Some(parent_resv) =3D config.parent_resv_obj { + // SAFETY: We have yet to expose the new gem object outside of= this function, so it is + // safe to modify this field. + unsafe { (*obj.obj.get()).base.resv =3D parent_resv.raw_dma_re= sv() }; + } + + // SAFETY: We have yet to expose this object outside of this funct= ion, so we're guaranteed + // to have exclusive access - thus making this safe to hold a muta= ble reference to. + let shmem =3D unsafe { &mut *obj.as_shmem() }; + shmem.set_map_wc(config.map_wc); + + Ok(obj) + } + + /// Returns the `Device` that owns this GEM object. + pub fn dev(&self) -> &device::Device { + // SAFETY: `dev` will have been initialized in `Self::new()` by `d= rm_gem_shmem_init()`. + unsafe { device::Device::from_raw((*self.as_raw()).dev) } + } + + extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { + // SAFETY: + // - 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) }; + + // 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(); + + // SAFETY: We're recovering the Kbox<> we created in gem_create_ob= ject() + let _ =3D unsafe { KBox::from_raw(this) }; + } + + /// 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. + #[inline] + pub fn sg_table(&self) -> Result<&scatterlist::SGTable> { + // SAFETY: + // - drm_gem_shmem_get_pages_sgt is thread-safe. + // - drm_gem_shmem_get_pages_sgt returns either a valid pointer to= a scatterlist, or an + // error pointer. + let sgt =3D from_err_ptr(unsafe { bindings::drm_gem_shmem_get_page= s_sgt(self.as_shmem()) })?; + + // SAFETY: We checked above that `sgt` is not an error pointer, so= it must be a valid + // pointer to a scatterlist + Ok(unsafe { scatterlist::SGTable::from_raw(sgt) }) + } +} + +impl Deref for Object { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Object { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Sealed for Object {} + +impl gem::IntoGEMObject for Object { + fn as_raw(&self) -> *mut bindings::drm_gem_object { + // SAFETY: + // - Our immutable reference is proof that this is safe to derefer= ence. + // - `obj` is always a valid drm_gem_shmem_object via our type inv= ariants. + unsafe { &raw mut (*self.obj.get()).base } + } + + unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Obje= ct { + // SAFETY: The safety contract of from_gem_obj() guarantees that `= obj` is contained within + // `Self` + unsafe { + let obj =3D Opaque::cast_from(container_of!(obj, bindings::drm= _gem_shmem_object, base)); + + &*container_of!(obj, Object, obj) + } + } +} + +impl driver::AllocImpl for Object { + type Driver =3D T::Driver; + + const ALLOC_OPS: driver::AllocOps =3D driver::AllocOps { + gem_create_object: None, + prime_handle_to_fd: None, + prime_fd_to_handle: None, + gem_prime_import: None, + gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_impo= rt_sg_table), + dumb_create: Some(bindings::drm_gem_shmem_dumb_create), + dumb_map_offset: None, + }; +} --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 23BCC27057D for ; Thu, 23 Oct 2025 21:26:52 +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=1761254813; cv=none; b=bCUPmTJdG4UK9iNCtnT7XyjIyI1+uKvL3uorhUhxgXdRao0/ndGfarK0jFB3GhqidOmwWwTpcnqCFkgxy1OzAKYksN445KDh+rkBEhOGJwooGNvCqDNYRk+7LqlxoAJ81GV1ljpJap14CyyDI1Nfboa3ng+G4NcCxW96sNEv9/k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254813; c=relaxed/simple; bh=F32XcE9uZaLeIAugKoH/5kYhwOSDtMq6/Znu/zqWWHs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IUXe85JPj6P+PLggaR7K2aPXAea8bTzVy9pb9ev5ZJNHVr4eM2YGnV6jFynqD5j5DU+g71Z5VwwwA/z2H8hXhzsP48TdPin7nJ7ZcY+lrCLl7s0PfCatZQUVi2nCNfcfp+Tm7JdVKSTBGOPOo523r9NvDjgV2qcN06SbR8B3Pb8= 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=Zh63lbpy; 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="Zh63lbpy" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254811; 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=zAmyMgT9xxjxNHcsyNCmwAK+5c4B/XqUwz4L+lbngcI=; b=Zh63lbpyMW97rBl0POvF+9OtMcdwjlj4yBboYtfX6RYw8mADyKMSc+0hmuF3HiLbqBATZE vYEVdj1hQ8lltE+zCAGNhqhmTAsrNVkWUVQibgK6pGbHDP55b+vtG89TPrkPJA0nYM0uRb Mvc09lU2/oeyW2UhwBVLtxhu0/uuvu0= 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-130-R200hXuuNxel_l9nS5WQ3w-1; Thu, 23 Oct 2025 17:26:47 -0400 X-MC-Unique: R200hXuuNxel_l9nS5WQ3w-1 X-Mimecast-MFC-AGG-ID: R200hXuuNxel_l9nS5WQ3w_1761254803 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 43E461975AF7; Thu, 23 Oct 2025 21:26:43 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5178230002FC; Thu, 23 Oct 2025 21:26:39 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: David Airlie , Simona Vetter , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Asahi Lina Subject: [PATCH v5 6/8] rust: drm: gem: Introduce shmem::SGTable Date: Thu, 23 Oct 2025 17:22:08 -0400 Message-ID: <20251023212540.1141999-7-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 Content-Type: text/plain; charset="utf-8" Currently we expose the ability to retrieve an SGTable for an shmem gem object using gem::shmem::Object::::sg_table(). However, this only gives us a borrowed reference. This being said - retrieving an SGTable is a fallible operation, and as such it's reasonable that a driver may want to hold onto an SGTable for longer then a reference would allow in order to avoid having to deal with fallibility every time they want to access the SGTable. One such driver with this usecase is the Asahi driver. So to support this, let's introduce shmem::SGTable - which both holds a pointer to the SGTable and a reference to its respective GEM object in order to keep the GEM object alive for as long as the shmem::SGTable. The type can be used identically to a normal SGTable. 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 rust/kernel/drm/gem/shmem.rs | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 45b95d60a3ec7..21ccb6c1824be 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -173,6 +173,25 @@ pub fn sg_table(&self) -> Result<&scatterlist::SGTable= > { // pointer to a scatterlist Ok(unsafe { scatterlist::SGTable::from_raw(sgt) }) } + + /// Creates (if necessary) and returns an owned reference to a scatter= -gather table of DMA pages + /// for this object. + /// + /// This is the same as [`sg_table`](Self::sg_table), except that it i= nstead returns an + /// [`shmem::SGTable`] which holds a reference to the associated gem o= bject, instead of a + /// reference to an [`scatterlist::SGTable`]. + /// + /// This will pin the object in memory. + /// + /// [`shmem::SGTable`]: SGTable + pub fn owned_sg_table(&self) -> Result> { + Ok(SGTable { + sgt: self.sg_table()?.into(), + // INVARIANT: We take an owned refcount to `self` here, ensuri= ng that `sgt` remains + // valid for as long as this `SGTable`. + _owner: self.into(), + }) + } } =20 impl Deref for Object { @@ -223,3 +242,34 @@ impl driver::AllocImpl for Object { dumb_map_offset: None, }; } + +/// 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 +/// [`scatterlist::SGTable`] referenced by this type remains valid for the= lifetime of this object. +/// +/// # Invariants +/// +/// - `sgt` is kept alive by `_owner`, ensuring it remains valid for as lo= ng as `Self`. +/// - `sgt` corresponds to the owned object in `_owner`. +/// - This object is only exposed in situations where we know the underlyi= ng `SGTable` will not be +/// modified for the lifetime of this object. Thus, it is safe to send/a= ccess this type across +/// threads. +pub struct SGTable { + sgt: NonNull, + _owner: ARef>, +} + +// SAFETY: This object is thread-safe via our type invariants. +unsafe impl Send for SGTable {} +// SAFETY: This object is thread-safe via our type invariants. +unsafe impl Sync for SGTable {} + +impl Deref for SGTable { + type Target =3D scatterlist::SGTable; + + fn deref(&self) -> &Self::Target { + // SAFETY: Creating an immutable reference to this is safe via our= type invariants. + unsafe { self.sgt.as_ref() } + } +} --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 23A7D2D12F3 for ; Thu, 23 Oct 2025 21:27:01 +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=1761254824; cv=none; b=E9X12QvstDS/tVIJqCCzHGRsFBLnrQrzUhxAoe1K66KtCo5W53PSx1izh5N3PBFGGenYDobVyzEz7wf9WeE6SRzHCEejSYt8IdxrCQ1uHz/44zGpxOmDSF4T193ZsoQkmbAZxabP7P4iPHvuZKhHIEDVCEYV/E7KOYgO6R8NeIE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254824; c=relaxed/simple; bh=tkKPywh710+WHjMFng00qEb8weT8b5fg7SsCIKD4WdQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=myCgWCAm3qCNsJpEP0ACbIDnrKrR7dO2sAui73rV2HMcLSnucvQoNBXQsWimD3rDEiR3oN4hWsz2ecSLNU5yiYqc7w8//LoJtJqd4eCtLa8ja5ZVSsgp8+kYRLrraR5keKFKjcRV83i7snZhSWi+cVA7H6Kcn/LgGg96V95apzc= 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=T+/YwPCW; 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="T+/YwPCW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254820; 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=ec9XuEAOd1i63X1HJfT6rmGoUxLBZchaMJ1ZJPi2fiM=; b=T+/YwPCWBq5fB3sZ5jfBA2LI1EDuXRMe6dOQzfUO8ddVnnDkv6GWlrH924McGXJ6RaTmOn x3scnQK3E3k/0HJiSx8jhR2lV8ptOfU1yjVFizs7cOX4hGdxXfxs2UE61RUggsyhfvdAZn T3Zt9W80BLEhSjhXLWl+vSU97UQXfms= 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-661-QZ_OnJohMFOQY0vSWSK_bg-1; Thu, 23 Oct 2025 17:26:57 -0400 X-MC-Unique: QZ_OnJohMFOQY0vSWSK_bg-1 X-Mimecast-MFC-AGG-ID: QZ_OnJohMFOQY0vSWSK_bg_1761254815 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-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B33AB1953988; Thu, 23 Oct 2025 21:26:53 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 04E4C30002EC; Thu, 23 Oct 2025 21:26:48 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Greg Kroah-Hartman , Viresh Kumar , FUJITA Tomonori , Krishna Ketan Rai , Wedson Almeida Filho , Tamir Duberstein Subject: [PATCH v5 7/8] rust: Introduce iosys_map bindings Date: Thu, 23 Oct 2025 17:22:09 -0400 Message-ID: <20251023212540.1141999-8-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 Content-Type: text/plain; charset="utf-8" This introduces a set of bindings for working with iosys_map in rust code. The design of this is heavily based off the design for both the io and dma_map bindings for Rust. Signed-off-by: Lyude Paul --- V5: - Fix incorrect field size being passed to iosys_map_memcpy_to() - Add an additional unit test, basic_macro(), which can successfully catch the above issue so it doesn't happen again in the future. rust/helpers/helpers.c | 1 + rust/helpers/iosys_map.c | 15 + rust/kernel/iosys_map.rs | 705 +++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 722 insertions(+) create mode 100644 rust/helpers/iosys_map.c create mode 100644 rust/kernel/iosys_map.rs diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 36d40f911345c..d549af697bd60 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -31,6 +31,7 @@ #include "irq.c" #include "fs.c" #include "io.c" +#include "iosys_map.c" #include "jump_label.c" #include "kunit.c" #include "maple_tree.c" diff --git a/rust/helpers/iosys_map.c b/rust/helpers/iosys_map.c new file mode 100644 index 0000000000000..b105261c3cf8a --- /dev/null +++ b/rust/helpers/iosys_map.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_off= set, + const void *src, size_t len) +{ + iosys_map_memcpy_to(dst, dst_offset, src, len); +} + +void rust_helper_iosys_map_memcpy_from(void *dst, const struct iosys_map *= src, + size_t src_offset, size_t len) +{ + iosys_map_memcpy_from(dst, src, src_offset, len); +} diff --git a/rust/kernel/iosys_map.rs b/rust/kernel/iosys_map.rs new file mode 100644 index 0000000000000..4da0ab57cf35c --- /dev/null +++ b/rust/kernel/iosys_map.rs @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! IO-agnostic memory mapping interfaces. +//! +//! This crate provides bindings for the `struct iosys_map` type, which pr= ovides a common interface +//! for memory mappings which can reside within coherent memory, or within= IO memory. +//! +//! C header: [`include/linux/iosys-map.h`](srctree/include/linux/pci.h) + +use crate::{ + prelude::*, + transmute::{AsBytes, FromBytes}, +}; +use bindings; +use core::{ + marker::PhantomData, + mem::{self, MaybeUninit}, + ops::{Deref, DerefMut, Range}, + slice, +}; + +/// Raw unsized representation of a `struct iosys_map`. +/// +/// This struct is a transparent wrapper around `struct iosys_map`. The C = API does not provide the +/// size of the mapping by default, and thus this type also does not inclu= de the size of the +/// mapping. As such, it cannot be used for actually accessing the underly= ing data pointed to by the +/// mapping. +/// +/// With the exception of kernel crates which may provide their own wrappe= rs around `RawIoSysMap`, +/// users will typically not interact with this type directly. +pub struct RawIoSysMap(bindings::iosys_map, Phanto= mData); + +impl RawIoSysMap { + /// Convert from a raw `bindings::iosys_map`. + #[expect(unused)] + #[inline] + pub(crate) fn from_raw(val: bindings::iosys_map) -> Self { + Self(val, PhantomData) + } + + /// Convert from a `RawIoSysMap` to a raw `bindings::iosys_map` ref. + #[inline] + pub(crate) fn as_raw(&self) -> &bindings::iosys_map { + &self.0 + } + + /// Convert from a `RawIoSysMap` to a raw mutable `bindings::iosys_= map` ref. + #[inline] + pub(crate) fn as_raw_mut(&mut self) -> &mut bindings::iosys_map { + &mut self.0 + } + + /// Returns whether the mapping is within IO memory space or not. + #[inline] + pub fn is_iomem(&self) -> bool { + self.0.is_iomem + } + + /// Returns the size of a single item in this mapping. + pub const fn item_size(&self) -> usize { + mem::size_of::() + } + + /// Returns a mutable address to the memory pointed to by this iosys m= ap. + /// + /// Note that this address is not guaranteed to reside in system memor= y, and may reside in IO + /// memory. + #[inline] + pub fn as_mut_ptr(&self) -> *mut T { + if self.is_iomem() { + // SAFETY: We confirmed above that this iosys map is contained= within iomem, so it's + // safe to read vaddr_iomem + unsafe { self.0.__bindgen_anon_1.vaddr_iomem } + } else { + // SAFETY: We confirmed above that this iosys map is not conta= ned within iomem, so it's + // safe to read vaddr. + unsafe { self.0.__bindgen_anon_1.vaddr } + } + .cast() + } + + /// Returns an immutable address to the memory pointed to by this iosy= s map. + /// + /// Note that this address is not guaranteed to reside in system memor= y, and may reside in IO + /// memory. + #[inline] + pub fn as_ptr(&self) -> *const T { + self.as_mut_ptr().cast_const() + } +} + +// SAFETY: As we make no guarantees about the validity of the mapping, the= re's no issue with sending +// this type between threads. +unsafe impl Send for RawIoSysMap {} + +impl Clone for RawIoSysMap { + fn clone(&self) -> Self { + Self(self.0, PhantomData) + } +} + +/// A sized version of a [`RawIoSysMap`]. +/// +/// Since this type includes the size of the [`RawIoSysMap`], it can be us= ed for accessing the +/// underlying data pointed to by it. +/// +/// # Invariants +/// +/// - The iosys mapping referenced by this type is guaranteed to be of at = least `size` bytes in +/// size +/// - The iosys mapping referenced by this type is valid for the lifetime = `'a`. +#[derive(Clone)] +pub struct IoSysMapRef<'a, T: AsBytes + FromBytes> { + map: RawIoSysMap, + size: usize, + _p: PhantomData<&'a T>, +} + +impl<'a, T: AsBytes + FromBytes> IoSysMapRef<'a, T> { + /// Create a new [`IoSysMapRef`] from a [`RawIoSysMap`]. + /// + /// # Safety + /// + /// - The caller guarantees that the mapping referenced by `map` is of= at least `size` bytes in + /// size. + /// - The caller guarantees that the mapping referenced by `map` remai= ns valid for the lifetime + /// of `'a`. + #[allow(unused)] + pub(crate) unsafe fn new(map: RawIoSysMap, size: usize) -> IoSysMap= Ref<'a, T> { + // INVARIANT: Our safety contract fulfills the type invariants of = `IoSysMapRef`. + IoSysMapRef { + map, + size, + _p: PhantomData, + } + } + + /// Return the size of the `IoSysMapRef`. + #[inline] + pub fn size(&self) -> usize { + self.size + } + + /// Returns an immutable reference slice to data from the region start= ing from `offset`. + /// + /// `offset` and `count` are in units of `T`. Note that this function = requires that the + /// underlying iosys mapping does not reside within iomem. + /// + /// This function can return the following errors: + /// + /// * [`ENOTSUPP`] if the memory region resides in iomem. + /// * [`EOVERFLOW`] if calculating the length of the slice results in = an overflow. + /// * [`EINVAL`] if the slice would go out of bounds of the memory reg= ion. + /// + /// # Safety + /// + /// * The caller promises that the memory pointed to by for this `IoSy= sMapRef` is not written to + /// while the returned slice is live. + /// * Callers must ensure that this call does not race with a write to= the same region while the + /// returned slice is alive. + /// + /// # Examples + /// + /// ``` + /// use kernel::iosys_map::*; + /// + /// # fn test() -> Result { + /// # let map =3D tests::VecIoSysMap::new(&[1, 2, 3])?; + /// # let map =3D map.get(); + /// // SAFETY: We are the only ones with access to `map`. + /// let slice =3D unsafe { map.as_slice(0, 3)? }; + /// assert_eq!(*slice, [1, 2, 3]); + /// + /// let slice =3D unsafe { map.as_slice(1, 2)? }; + /// assert_eq!(*slice, [2, 3]); + /// # Ok::<(), Error>(()) } + /// # assert!(test().is_ok()); + /// ``` + pub unsafe fn as_slice(&self, offset: usize, count: usize) -> Result<&= [T]> { + if self.is_iomem() { + return Err(ENOTSUPP); + } + + let range =3D self.validate_range(offset, count)?; + + // SAFETY: + // * `self.validate_range()` is guaranteed to return a range withi= n this memory allocation + // that is contained within the iosys_map and is properly aligne= d to the size of + // `T`. + // * We checked above that the memory pointed to by this iosys map= doesn't reside in iomem, + // so it must reside in system memory - ensuring that `self.addr= _mut()` returns a valid + // virtual memory address. + Ok(unsafe { slice::from_raw_parts(self.as_ptr().byte_add(range.sta= rt), count) }) + } + + /// Returns a mutable reference slice to data from the region starting= from `offset`. + /// + /// `offset` and `count` are in units of `T`. Note that this function = requires that the + /// underlying iosys mapping does not reside within iomem. + /// + /// For a list of errors this function can return, see [`as_slice`](Se= lf::as_slice). + /// + /// # Safety + /// + /// The caller promises that the memory region pointed to by this `IoS= ysMapRef` is not written + /// to or read from while the returned slice is live. + pub unsafe fn as_mut_slice(&mut self, offset: usize, count: usize) -> = Result<&mut [T]> { + if self.is_iomem() { + return Err(ENOTSUPP); + } + + let range =3D self.validate_range(offset, count)?; + + // SAFETY: + // * `self.validate_range()` is guaranteed to return a range withi= n this memory allocation + // that is contained within the iosys_map and is properly aligne= d to the size of + // `T`. + // * We checked above that the memory pointed to by this iosys map= doesn't reside in iomem, + // so it must reside in system memory - ensuring that `self.addr= _mut()` returns a valid + // virtual memory address. + Ok(unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().byte_add(r= ange.start), count) }) + } + + /// Writes `src` to the region starting from `offset`. + /// + /// `offset` is in units of `T`, not the number of bytes. + /// + /// This function can return the following errors: + /// + /// * [`EOVERFLOW`] if calculating the length of the slice results in = an overflow. + /// * [`EINVAL`] if the slice would go out of bounds of the memory reg= ion. + /// + /// # Examples + /// + /// ``` + /// use kernel::iosys_map::*; + /// + /// # fn test() -> Result { + /// # let map =3D tests::VecIoSysMap::new(&[0; 3])?; + /// # let mut map =3D map.get(); + /// map.write(&[1, 2, 3], 0)?; // (now [1, 2, 3]) + /// map.write(&[4], 2)?; // (now [1, 2, 4]) + /// + /// // SAFETY: We are the only ones with access to `map` + /// let slice =3D unsafe { map.as_slice(0, 3)? }; + /// assert_eq!(slice, [1, 2, 4]); + /// + /// # Ok::<(), Error>(()) } + /// # assert!(test().is_ok()); + /// ``` + pub fn write(&mut self, src: &[T], offset: usize) -> Result { + let range =3D self.validate_range(offset, src.len())?; + + // SAFETY: + // - The address pointed to by this iosys_map is guaranteed to be = valid via IoSysMapRef's + // type invariants. + // - `self.validate_range()` always returns a valid range of memor= y within said memory. + unsafe { + bindings::iosys_map_memcpy_to( + self.as_raw_mut(), + range.start, + src.as_ptr().cast(), + range.len(), + ) + }; + + Ok(()) + } + + /// Attempt to compute the offset of an item within the iosys map usin= g its index. + /// + /// Returns an error if an overflow occurs. + /// + /// # Safety + /// + /// This function checks for overflows, but it explicitly does not che= ck if the offset goes out + /// of bounds. It is the caller's responsibility to check for this bef= ore using the returned + /// offset with the iosys_map API. + unsafe fn item_from_index(&self, idx: usize) -> Result { + self.item_size().checked_mul(idx).ok_or(EOVERFLOW) + } + + /// Common helper to compute and validate a range for a specific data = type applied from + /// within the allocated region of the iosys mapping. + /// + /// This function returns the computed range if it doesn't overflow, a= nd the range is valid + /// within the allocated region of the iosys mapping. This is so that = the computation may + /// be reused. + /// + /// On success, the range returned by this function is guaranteed: + /// + /// * To be a valid range of memory within the virtual mapping for thi= s gem object. + /// * To be properly aligned to [`RawIoSysMap::item_size()`]. + fn validate_range(&self, offset: usize, count: usize) -> Result> { + // SAFETY: If the offset is out of bounds, we'll catch this via ov= erflow checks or when + // checking range_end. + let offset =3D unsafe { self.item_from_index(offset)? }; + let range_size =3D count.checked_mul(self.item_size()).ok_or(EOVER= FLOW)?; + let range_end =3D offset.checked_add(range_size).ok_or(EOVERFLOW)?; + + if range_end > self.size() { + return Err(EINVAL); + } + + // INVARIANT: Since `offset` and `count` are both in units of `T`,= we're guaranteed that the + // range returned here is properly aligned to `T`. + Ok(offset..range_end) + } + + /// Common helper to compute the memory address of an item within the = iosys mapping. + /// + /// Public but hidden, since it should only be used from [`iosys_map_r= ead`] and + /// [`iosys_map_write`]. + #[doc(hidden)] + pub fn ptr_from_index(&self, offset: usize) -> Result<*mut T> { + // SAFETY: We check if the resulting offset goes out of bounds bel= ow. + let offset =3D unsafe { self.item_from_index(offset)? }; + + if offset.checked_add(self.item_size()).ok_or(EOVERFLOW)? > self.s= ize() { + return Err(EINVAL); + } + + // SAFETY: We confirmed that `offset` + the item size does not go = out of bounds above. + Ok(unsafe { self.as_mut_ptr().byte_add(offset) }) + } + + // TODO: + // This function is currently needed for making the iosys_map_read!() = and iosys_map_write!() + // macros work due to a combination of a few limitations: + // + // * The current C API for iosys_map requires that we use offsets for = reading/writing + // iosys_maps. + // * Calculating the offset of a field within a struct requires that w= e either: + // * Use field projection for calculating the offset of the field. W= e don't have this yet. + // * Explicitly specify the type of the struct, which would be cumbe= rsome to require in the + // read/write macros. + // * Provide a typed pointer (or other reference) to the struct in q= uestion, allowing the + // use of &raw const and &raw mut. + // * Keep in mind: we can't simply cast the offset of an item in t= he iosys map into a typed + // pointer to fulfill the third option. While having invalid mem= ory addresses as pointers + // is ok, adding an offset to a pointer in rust requires that th= e resulting memory address + // is within the same allocation. Since an invalid pointer has n= o allocation, we can't + // make that guarantee. + // + // So, until we have field projection the way we workaround this: + // + // * Calculate the offset (self.item_from_index()) of the struct withi= n the iosys map + // * Calculate the memory address of the struct using the offset from = the last step + // (self.ptr_from_index()). + // * Use that memory address with &raw const/&raw mut in order to calc= ulate the memory address + // of the desired field, ensuring it remains in the same allocation = (happens within the + // macros). + // * Convert the address from the last step back into an offset within= the iosys map + // (offset_from_ptr()). + // + // Once we do get field projection, this silly code should be removed. + // + /// Convert a pointer to an item within the iosys map back into an off= set. + /// + /// # Safety + /// + /// `ptr` must be a valid pointer to data within the iosys map. + unsafe fn offset_from_ptr(&self, ptr: *const F) -> usize { + // SAFETY: `ptr` always points to data within the memory pointed t= o by the iosys map, + // meaning it is within the same memory allocation. + // + // Additionally, since `ptr` is within the iosys mapping, the offs= et here will always be + // positive and safe to cast to a usize. + // (TODO: replace this with byte_offset_from_unsigned once it's av= ailable in the kernel) + unsafe { ptr.byte_offset_from(self.as_ptr()) as usize } + } + + /// Reads the value of `field` and ensures that its type is [`FromByte= s`]. + /// + /// # Safety + /// + /// This must be called from the [`iosys_map_read`] macro which ensure= s that the `field` + /// pointer is validated beforehand. + /// + /// Public but hidden since it should only be used from the [`iosys_ma= p_read`] macro. + #[doc(hidden)] + pub unsafe fn field_read(&self, field: *const F) -> F { + let mut field_val =3D MaybeUninit::::uninit(); + + // SAFETY: `field` is guaranteed valid via our safety contract. + let offset =3D unsafe { self.offset_from_ptr(field) }; + + // SAFETY: Since we verified `field` is valid above, `offset_from_= ptr` will always return a + // valid offset within the iosys map. + unsafe { + bindings::iosys_map_memcpy_from( + field_val.as_mut_ptr().cast(), + self.as_raw(), + offset, + mem::size_of::(), + ) + } + + // SAFETY: We just initialized `field_val` above. + unsafe { field_val.assume_init() } + } + + /// Writes the value of `field` and ensures that its type is [`AsBytes= `]. + /// + /// # Safety + /// + /// This must be called from the [`iosys_map_write`] macro which ensur= es that the `field` + /// pointers validated beforehand. + /// + /// Public but hidden since it should only be used from the [`iosys_ma= p_write`] macro. + #[doc(hidden)] + pub unsafe fn field_write(&mut self, field: *mut F, val: F= ) { + // SAFETY: `field` is guaranteed valid via our safety contract. + let offset =3D unsafe { self.offset_from_ptr(field) }; + + // SAFETY: `offset_from_ptr` always returns a valid offset within = the iosys map. + unsafe { + bindings::iosys_map_memcpy_to( + self.as_raw_mut(), + offset, + core::ptr::from_ref(&val).cast(), + mem::size_of::(), + ) + } + } +} + +impl<'a, T: AsBytes + FromBytes> Deref for IoSysMapRef<'a, T> { + type Target =3D RawIoSysMap; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +impl<'a, T: AsBytes + FromBytes> DerefMut for IoSysMapRef<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.map + } +} + +/// Reads from a field of an item from an iosys map ref. +/// +/// # Examples +/// +/// ``` +/// use kernel::{iosys_map::*, transmute::*}; +/// +/// #[derive(Copy, Clone, Debug, PartialEq, Eq)] +/// struct MyStruct { a: u32, b: u16 } +/// +/// // SAFETY: All bit patterns are acceptable values for `MyStruct`. +/// unsafe impl FromBytes for MyStruct {}; +/// // SAFETY: Instances of `MyStruct` have no uninitialized portions. +/// unsafe impl AsBytes for MyStruct {}; +/// +/// # fn test() -> Result { +/// # let map =3D tests::VecIoSysMap::new(&[MyStruct { a: 42, b: 2 }; 3])?; +/// # let map =3D map.get(); +/// let whole =3D kernel::iosys_map_read!(map[2])?; +/// assert_eq!(whole, MyStruct { a: 42, b: 2 }); +/// +/// let field =3D kernel::iosys_map_read!(map[1].b)?; +/// assert_eq!(field, 2); +/// # Ok::<(), Error>(()) } +/// # assert!(test().is_ok()); +/// ``` +#[macro_export] +macro_rules! iosys_map_read { + ($map:expr, $idx:expr, $($field:tt)*) =3D> {{ + (|| -> ::core::result::Result<_, $crate::error::Error> { + let map =3D &$map; + let item =3D $crate::iosys_map::IoSysMapRef::ptr_from_index(ma= p, $idx)?; + + // SAFETY: `ptr_from_index()` ensures that `item` is always a = valid (although + // potentially not dereferenceable, which is fine here) pointe= r to within the iosys + // mapping. + unsafe { + let ptr_field =3D &raw const (*item) $($field)*; + ::core::result::Result::Ok( + $crate::iosys_map::IoSysMapRef::field_read(map, ptr_fi= eld) + ) + } + })() + }}; + ($map:ident [ $idx: expr ] $($field:tt)* ) =3D> { + $crate::iosys_map_read!($map, $idx, $($field)*) + }; + ($($map:ident).* [ $idx:expr ] $($field:tt)* ) =3D> { + $crate::iosys_map_read!($($map).*, $idx, $($field)*) + }; +} + +/// Writes to a field of an item from an iosys map ref. +/// +/// # Examples +/// +/// ``` +/// use kernel::{iosys_map::*, transmute::*}; +/// +/// #[derive(Copy, Clone, Debug, PartialEq, Eq)] +/// struct MyStruct { a: u32, b: u16 }; +/// +/// // SAFETY: All bit patterns are acceptable values for `MyStruct`. +/// unsafe impl FromBytes for MyStruct {}; +/// // SAFETY: Instances of `MyStruct` have no uninitialized portions. +/// unsafe impl AsBytes for MyStruct {}; +/// +/// # fn test() -> Result { +/// # let map =3D tests::VecIoSysMap::new(&[MyStruct { a: 42, b: 2 }; 3])?; +/// # let mut map =3D map.get(); +/// kernel::iosys_map_write!(map[2].b =3D 1337)?; +/// # assert_eq!(kernel::iosys_map_read!(map[2].b)?, 1337); +/// +/// kernel::iosys_map_write!(map[1] =3D MyStruct { a: 10, b: 20 })?; +/// # assert_eq!(kernel::iosys_map_read!(map[1])?, MyStruct { a: 10, b: 20= }); +/// # Ok::<(), Error>(()) } +/// # assert!(test().is_ok()); +/// ``` +#[macro_export] +macro_rules! iosys_map_write { + ($map:ident [ $idx:expr ] $($field:tt)*) =3D> {{ + $crate::iosys_map_write!($map, $idx, $($field)*) + }}; + ($($map:ident).* [ $idx:expr ] $($field:tt)* ) =3D> {{ + $crate::iosys_map_write!($($map).*, $idx, $($field)*) + }}; + ($map:expr, $idx:expr, =3D $val:expr) =3D> { + (|| -> ::core::result::Result<_, $crate::error::Error> { + // (expand these outside of the unsafe block (clippy::macro-me= tavars-in-unsafe) + let map =3D &mut $map; + let val =3D $val; + + let item =3D $crate::iosys_map::IoSysMapRef::ptr_from_index(ma= p, $idx)?; + // SAFETY: `item_from_index` ensures that `item` is always a v= alid item. + unsafe { $crate::iosys_map::IoSysMapRef::field_write(map, item= , val) }; + ::core::result::Result::Ok(()) + })() + }; + ($map:expr, $idx:expr, $(.$field:ident)* =3D $val:expr) =3D> { + (|| -> ::core::result::Result<_, $crate::error::Error> { + // (expand these outside of the unsafe block (clippy::macro-me= tavars-in-unsafe) + let map =3D &mut $map; + let val =3D $val; + + let item =3D $crate::iosys_map::IoSysMapRef::ptr_from_index(ma= p, $idx)?; + + // SAFETY: `ptr_from_index()` ensures that `item` is always a = valid (although + // potentially not dereferenceable, which is fine here) pointe= r to within the iosys + // mapping. + unsafe { + let ptr_field =3D &raw mut (*item) $(.$field)*; + $crate::iosys_map::IoSysMapRef::field_write(map, ptr_field= , val) + }; + ::core::result::Result::Ok(()) + })() + }; +} + +#[doc(hidden)] +#[kunit_tests(rust_iosys_map)] +pub mod tests { + use super::*; + + /// A helper struct for managed IoSysMapRef structs which point to a [= `Vec`]. + pub struct VecIoSysMap { + map: RawIoSysMap, + vec: KVec, + } + + impl VecIoSysMap { + pub fn new(src: &[T]) -> Result { + let mut vec =3D KVec::::new(); + + vec.extend_from_slice(src, GFP_KERNEL)?; + + let map =3D RawIoSysMap( + bindings::iosys_map { + is_iomem: false, + __bindgen_anon_1: bindings::iosys_map__bindgen_ty_1 { + vaddr: vec.as_mut_ptr().cast(), + }, + }, + PhantomData, + ); + + Ok(Self { map, vec }) + } + + pub fn get(&self) -> IoSysMapRef<'_, T> { + // SAFETY: `map` points to `vec`, so the size of `map` is the = size of the `vec`. + unsafe { IoSysMapRef::new(self.map.clone(), self.vec.len() * s= elf.map.item_size()) } + } + } + + #[test] + fn basic() -> Result { + let map =3D VecIoSysMap::new(&[0; 3])?; + let mut map =3D map.get(); + + map.write(&[1, 2, 3], 0)?; + + // SAFETY: We are the only ones with access to map. + assert_eq!(unsafe { map.as_slice(0, 3)? }, [1, 2, 3]); + + map.write(&[42], 1)?; + + // SAFETY: We are the only ones with access to the map. + assert_eq!(unsafe { map.as_slice(0, 3)? }, [1, 42, 3]); + + // SAFETY: We are the only ones with access to the map. + assert_eq!(unsafe { map.as_slice(1, 1)? }, [42]); + + Ok(()) + } + + #[test] + fn oob_accesses() -> Result { + let map =3D VecIoSysMap::new(&[0; 3])?; + let mut map =3D map.get(); + + // SAFETY: We are the only ones with access to map. + assert!(unsafe { map.as_slice(0, 4) }.is_err()); + + // SAFETY: We are the only ones with access to map. + assert!(unsafe { map.as_slice(1, 3) }.is_err()); + + assert!(map.write(&[1, 2, 3, 69], 0).is_err()); + assert!(map.write(&[1, 2, 3], 69).is_err()); + + Ok(()) + } + + #[test] + fn overflows() -> Result { + let map =3D VecIoSysMap::new(&[0; 3])?; + let mut map =3D map.get(); + + // SAFETY: We are the only ones with access to map. + assert!(unsafe { map.as_slice(usize::MAX, 3) }.is_err()); + + // SAFETY: We are the only ones with access to map. + assert!(unsafe { map.as_slice(0, usize::MAX) }.is_err()); + + assert!(map.write(&[1, 2, 3], usize::MAX).is_err()); + + Ok(()) + } + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + struct TestStruct { + a: u32, + b: u64, + } + + // SAFETY: All bit patterns are acceptable values for `TestStruct`. + unsafe impl FromBytes for TestStruct {} + // SAFETY: Instances of `TestStruct` have no uninitialized portions. + unsafe impl AsBytes for TestStruct {} + + #[test] + fn basic_macro() -> Result { + let mut expected =3D [TestStruct { a: 1, b: 2 }; 5]; + let map =3D VecIoSysMap::new(&expected)?; + let mut map =3D map.get(); + + iosys_map_write!(map[3].a =3D u32::MAX)?; + expected[3].a =3D u32::MAX; + + assert_eq!(iosys_map_read!(map[3].a)?, u32::MAX); + assert_eq!(iosys_map_read!(map[3])?, TestStruct { a: u32::MAX, b: = 2 }); + + // Compare the entire array, so that we catch any mis-sized writes. + // SAFETY: We are the only ones with access to map. + assert_eq!(expected, unsafe { map.as_slice(0, 5)? }); + + Ok(()) + } + + #[test] + fn macro_oob_accesses() -> Result { + let map =3D VecIoSysMap::new(&[TestStruct { a: 1, b: 2 }; 3])?; + let mut map =3D map.get(); + + assert!(iosys_map_read!(map[5].b).is_err()); + assert!(iosys_map_read!(map[1000]).is_err()); + assert!(iosys_map_write!(map[6969].a =3D 999).is_err()); + assert!(iosys_map_write!(map[243] =3D TestStruct { a: 99, b: 22 })= .is_err()); + + Ok(()) + } + + #[test] + fn macro_overflows() -> Result { + let map =3D VecIoSysMap::new(&[TestStruct { a: 1, b: 2 }; 3])?; + let mut map =3D map.get(); + + assert!(iosys_map_read!(map[usize::MAX]).is_err()); + assert!(iosys_map_read!(map[usize::MAX].b).is_err()); + assert!(iosys_map_write!(map[usize::MAX] =3D TestStruct { a: 1, b:= 1 }).is_err()); + assert!(iosys_map_write!(map[usize::MAX].b =3D 1).is_err()); + + Ok(()) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3dd7bebe78882..a10d50076c872 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -98,6 +98,7 @@ pub mod init; pub mod io; pub mod ioctl; +pub mod iosys_map; pub mod iov; pub mod irq; pub mod jump_label; --=20 2.51.0 From nobody Sat Feb 7 18:21:14 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 5F3E5277037 for ; Thu, 23 Oct 2025 21:27: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=1761254834; cv=none; b=em+TiQe19O3zzNh4p45GLErt3/8V/kQ+We7F4geAcncrGXqsRxbRGf4+P4DNCIN+Lb+Q3mEfoISCc5YfLq4VburjNjC5UecQaGHc9EP9iwuQRpB+8r3WziYuDPPiuYIqQhB0tKL6Zduy8ssH3fZeK995DKTZdQDChjWzENNts1o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1761254834; c=relaxed/simple; bh=qf6a23PTN6BJbtTf8XRrDB2SB4SFxvK4SN0a7l+wXvs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UGAsydLR3tNlzEEnN7TDcWy9NIjj1SuVtuiP3FzJDZyqCn56fkpCGsK3w+VDBhjZFSCO3TlP5leolKyjVU5ZvJbBYXcB8IobI2Dpm5dzel1o0umKnac1FvaJN3L8vBdz8U8fCOjt3/sEhbOrOQINNkxw9OFvHX+QpMRewibbnc0= 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=NT8SbYPR; 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="NT8SbYPR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1761254831; 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=fgB8Z93TRo4WCd3p++Pa+dxvLDuIIdiTgas1fg3xpX4=; b=NT8SbYPRYA9v9J7dKNK4AZ8h78wGV+8eG/G/D78qzzpxRKizhpGtJROf1IQbietltPPpvr t9LihVvifoZXjEbgB6+NHLumnaJGwzRA8cMUBEaZUi/nLPkaqQ2DB2ULOTmvDPhdsYRG+A Om0u9opXo9x36ZPy12pVqt9dAXkgCRM= 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-108-DM6r9jKOOTedqlgtO7uMLQ-1; Thu, 23 Oct 2025 17:27:05 -0400 X-MC-Unique: DM6r9jKOOTedqlgtO7uMLQ-1 X-Mimecast-MFC-AGG-ID: DM6r9jKOOTedqlgtO7uMLQ_1761254823 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-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 28A701956046; Thu, 23 Oct 2025 21:27:03 +0000 (UTC) Received: from chopper.lan (unknown [10.22.64.235]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CC7A730002DB; Thu, 23 Oct 2025 21:26:59 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Alice Ryhl , Daniel Almeida , Danilo Krummrich , linux-kernel@vger.kernel.org Cc: David Airlie , Simona Vetter , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Asahi Lina Subject: [PATCH v5 8/8] rust: drm/gem: Add vmap functions to shmem bindings Date: Thu, 23 Oct 2025 17:22:10 -0400 Message-ID: <20251023212540.1141999-9-lyude@redhat.com> In-Reply-To: <20251023212540.1141999-1-lyude@redhat.com> References: <20251023212540.1141999-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.4 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 --- rust/kernel/drm/gem/shmem.rs | 160 ++++++++++++++++++++++++++++++++++- rust/kernel/iosys_map.rs | 2 +- 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 21ccb6c1824be..62a2c12b9fe2a 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -13,15 +13,18 @@ container_of, drm::{device, driver, gem, private::Sealed}, error::{from_err_ptr, to_result}, + iosys_map::*, prelude::*, scatterlist, + transmute::*, types::{ARef, Opaque}, }; use core::{ + mem::{self, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, }; -use gem::{BaseObjectPrivate, DriverObject, IntoGEMObject}; +use gem::{BaseObject, BaseObjectPrivate, DriverObject, IntoGEMObject}; =20 /// A struct for controlling the creation of shmem-backed GEM objects. /// @@ -192,6 +195,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> { + build_assert!( + mem::size_of::() > 0, + "It doesn't make sense for the mapping type to be a ZST" + ); + + 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 RawIoSys= Map) { + 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>= { + Ok(VMap { + map: self.raw_vmap()?, + owner: self.into(), + }) + } } =20 impl Deref for Object { @@ -243,6 +312,95 @@ 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, T: AsBytes + FromBytes> { + map: IoSysMapRef<'a, T>, + owner: &'a Object, +} + +impl<'a, D: DriverObject, T: AsBytes + FromBytes> Clone for VMapRef<'a, D,= T> { + 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, T: AsBytes + FromBytes> Deref for VMapRef<'a, D,= T> { + type Target =3D IoSysMapRef<'a, T>; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +impl<'a, D: DriverObject, T: AsBytes + FromBytes> DerefMut for VMapRef<'a,= D, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.map + } +} + +impl<'a, D: DriverObject, T: AsBytes + FromBytes> Drop for VMapRef<'a, D, = T> { + 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) }; + } +} + +/// An owned reference to a virtual mapping for a shmem-based GEM object i= n kernel address space. +/// +/// # Invariants +/// +/// - 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, T: AsBytes + FromBytes> From> = for VMap { + fn from(value: VMapRef<'a, D, T>) -> Self { + let this =3D Self { + map: value.map.clone(), + owner: value.owner.into(), + }; + + mem::forget(value); + this + } +} + +impl VMap { + /// Return a reference to the iosys map for this `VMap`. + pub fn get(&self) -> IoSysMapRef<'_, T> { + // SAFETY: The size of the iosys_map is equivalent to the size of = the gem object. + unsafe { IoSysMapRef::new(self.map.clone(), self.owner.size()) } + } + + /// 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 4da0ab57cf35c..e7540139ada01 100644 --- a/rust/kernel/iosys_map.rs +++ b/rust/kernel/iosys_map.rs @@ -32,7 +32,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, PhantomData) --=20 2.51.0