[RFC PATCH 7/9] drm/rustgem: implement a Rust version of VGEM
Maíra Canal
mcanal at igalia.com
Fri Mar 17 12:12:11 UTC 2023
The VGEM driver is a non-hardware backed GEM service and it is currently
implemented in C on the DRM subsystem. This patch introduces a new
version of the VGEM driver written in Rust. This driver provides the
same functionalities and uses the same UAPI of the original VGEM driver.
Signed-off-by: Maíra Canal <mcanal at igalia.com>
---
drivers/gpu/drm/Kconfig | 11 +++
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/rustgem/Makefile | 3 +
drivers/gpu/drm/rustgem/fence.rs | 56 ++++++++++++++
drivers/gpu/drm/rustgem/file.rs | 128 +++++++++++++++++++++++++++++++
drivers/gpu/drm/rustgem/gem.rs | 31 ++++++++
drivers/gpu/drm/rustgem/vgem.rs | 104 +++++++++++++++++++++++++
rust/bindings/bindings_helper.h | 1 +
8 files changed, 335 insertions(+)
create mode 100644 drivers/gpu/drm/rustgem/Makefile
create mode 100644 drivers/gpu/drm/rustgem/fence.rs
create mode 100644 drivers/gpu/drm/rustgem/file.rs
create mode 100644 drivers/gpu/drm/rustgem/gem.rs
create mode 100644 drivers/gpu/drm/rustgem/vgem.rs
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 315cbdf61979..0f886a33e377 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -245,6 +245,17 @@ source "drivers/gpu/drm/kmb/Kconfig"
config DRM_VGEM
tristate "Virtual GEM provider"
+ depends on !RUST
+ depends on DRM && MMU
+ select DRM_GEM_SHMEM_HELPER
+ help
+ Choose this option to get a virtual graphics memory manager,
+ as used by Mesa's software renderer for enhanced performance.
+ If M is selected the module will be called vgem.
+
+config DRM_RUSTGEM
+ tristate "Virtual GEM provider written in Rust"
+ depends on RUST
depends on DRM && MMU
select DRM_GEM_SHMEM_HELPER
help
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index cc637343d87b..8bfd40a2d341 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_VGEM) += vgem/
+obj-$(CONFIG_DRM_RUSTGEM) += rustgem/
obj-$(CONFIG_DRM_VKMS) += vkms/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
diff --git a/drivers/gpu/drm/rustgem/Makefile b/drivers/gpu/drm/rustgem/Makefile
new file mode 100644
index 000000000000..94b67cec0377
--- /dev/null
+++ b/drivers/gpu/drm/rustgem/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_RUSTGEM) += vgem.o
diff --git a/drivers/gpu/drm/rustgem/fence.rs b/drivers/gpu/drm/rustgem/fence.rs
new file mode 100644
index 000000000000..9ef1399548e2
--- /dev/null
+++ b/drivers/gpu/drm/rustgem/fence.rs
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MIT
+
+use core::fmt::Write;
+use core::ops::Deref;
+use kernel::c_str;
+use kernel::dma_fence::*;
+use kernel::prelude::*;
+use kernel::sync::Arc;
+
+static QUEUE_NAME: &CStr = c_str!("vgem_fence");
+static QUEUE_CLASS_KEY: kernel::sync::LockClassKey = kernel::sync::LockClassKey::new();
+
+pub(crate) struct Fence {}
+
+#[vtable]
+impl FenceOps for Fence {
+ const USE_64BIT_SEQNO: bool = false;
+
+ fn get_driver_name<'a>(self: &'a FenceObject<Self>) -> &'a CStr {
+ c_str!("vgem")
+ }
+
+ fn get_timeline_name<'a>(self: &'a FenceObject<Self>) -> &'a CStr {
+ c_str!("unbound")
+ }
+
+ fn fence_value_str(self: &FenceObject<Self>, output: &mut dyn Write) {
+ let _ = output.write_fmt(format_args!("{}", self.seqno()));
+ }
+
+ fn timeline_value_str(self: &FenceObject<Self>, output: &mut dyn Write) {
+ let value = if self.is_signaled() { self.seqno() } else { 0 };
+ let _ = output.write_fmt(format_args!("{}", value));
+ }
+}
+
+pub(crate) struct VgemFence {
+ fence: Arc<UniqueFence<Fence>>,
+}
+
+impl VgemFence {
+ pub(crate) fn create() -> Result<Self> {
+ let fence_ctx = FenceContexts::new(1, QUEUE_NAME, &QUEUE_CLASS_KEY)?;
+ let fence = Arc::try_new(fence_ctx.new_fence(0, Fence {})?)?;
+
+ Ok(Self { fence })
+ }
+}
+
+impl Deref for VgemFence {
+ type Target = UniqueFence<Fence>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.fence
+ }
+}
diff --git a/drivers/gpu/drm/rustgem/file.rs b/drivers/gpu/drm/rustgem/file.rs
new file mode 100644
index 000000000000..2552c7892b0e
--- /dev/null
+++ b/drivers/gpu/drm/rustgem/file.rs
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: MIT
+
+use crate::fence::VgemFence;
+use crate::gem::DriverObject;
+use crate::{VgemDevice, VgemDriver};
+use core::ops::Deref;
+use kernel::dma_fence::RawDmaFence;
+use kernel::drm::gem::BaseObject;
+use kernel::prelude::*;
+use kernel::{bindings, drm, drm::gem::shmem, xarray};
+
+pub(crate) struct File {
+ fences: xarray::XArray<Box<Option<VgemFence>>>,
+}
+
+/// Convenience type alias for our DRM `File` type.
+pub(crate) type DrmFile = drm::file::File<File>;
+
+impl drm::file::DriverFile for File {
+ type Driver = VgemDriver;
+
+ fn open(_device: &VgemDevice) -> Result<Box<Self>> {
+ Ok(Box::try_new(Self {
+ fences: xarray::XArray::new(xarray::flags::ALLOC1)?,
+ })?)
+ }
+}
+
+impl File {
+ /// vgem_fence_attach_ioctl (DRM_IOCTL_VGEM_FENCE_ATTACH):
+ ///
+ /// Create and attach a fence to the vGEM handle. This fence is then exposed
+ /// via the dma-buf reservation object and visible to consumers of the exported
+ /// dma-buf.
+ ///
+ /// If the vGEM handle does not exist, attach returns -ENOENT.
+ ///
+ pub(crate) fn attach(
+ _device: &VgemDevice,
+ data: &mut bindings::drm_vgem_fence_attach,
+ file: &DrmFile,
+ ) -> Result<u32> {
+ if (data.flags & !bindings::VGEM_FENCE_WRITE) != 0 {
+ return Err(EINVAL);
+ }
+
+ if data.pad != 0 {
+ return Err(EINVAL);
+ }
+
+ let obj = shmem::Object::<DriverObject>::lookup_handle(file, data.handle)?;
+
+ let fence = VgemFence::create()?;
+
+ // Check for a conflicting fence
+ let resv = obj.resv();
+ let usage = resv.usage_rw(data.flags & bindings::VGEM_FENCE_WRITE != 0);
+ if !resv.test_signaled(usage) {
+ fence.signal()?;
+ return Err(EBUSY);
+ }
+
+ let usage = if (data.flags & bindings::VGEM_FENCE_WRITE) != 0 {
+ bindings::dma_resv_usage_DMA_RESV_USAGE_WRITE
+ } else {
+ bindings::dma_resv_usage_DMA_RESV_USAGE_READ
+ };
+
+ // Expose the fence via the dma-buf
+ if resv.add_fences(fence.deref(), 1, usage).is_ok() {
+ // Record the fence in our xarray for later signaling
+ if let Ok(id) = file.fences.alloc(Some(Box::try_new(Some(fence))?)) {
+ data.out_fence = id as u32
+ }
+ } else {
+ fence.signal()?;
+ }
+
+ Ok(0)
+ }
+
+ /// vgem_fence_signal_ioctl (DRM_IOCTL_VGEM_FENCE_SIGNAL):
+ ///
+ /// Signal and consume a fence earlier attached to a vGEM handle using
+ /// attach (DRM_IOCTL_VGEM_FENCE_ATTACH).
+ ///
+ /// Signaling a fence indicates to all consumers of the dma-buf that the
+ /// client has completed the operation associated with the fence, and that the
+ /// buffer is then ready for consumption.
+ ///
+ /// If the fence does not exist (or has already been signaled by the client),
+ /// signal returns -ENOENT.
+ ///
+ pub(crate) fn signal(
+ _device: &VgemDevice,
+ data: &mut bindings::drm_vgem_fence_signal,
+ file: &DrmFile,
+ ) -> Result<u32> {
+ if data.flags != 0 {
+ return Err(EINVAL);
+ }
+
+ let fence = file
+ .fences
+ .replace(data.fence as usize, Box::try_new(None)?);
+
+ let fence = match fence {
+ Err(ret) => {
+ return Err(ret);
+ }
+ Ok(None) => {
+ return Err(ENOENT);
+ }
+ Ok(fence) => {
+ let fence = fence.unwrap().unwrap();
+
+ if fence.is_signaled() {
+ return Err(ETIMEDOUT);
+ }
+
+ fence
+ }
+ };
+
+ fence.signal()?;
+ Ok(0)
+ }
+}
diff --git a/drivers/gpu/drm/rustgem/gem.rs b/drivers/gpu/drm/rustgem/gem.rs
new file mode 100644
index 000000000000..e20bfe4ee0cf
--- /dev/null
+++ b/drivers/gpu/drm/rustgem/gem.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: MIT
+
+use kernel::{
+ drm::{gem, gem::shmem},
+ error::Result,
+};
+
+use crate::file::DrmFile;
+use crate::{VgemDevice, VgemDriver};
+
+/// Represents the inner data of a GEM object for this driver.
+pub(crate) struct DriverObject {}
+
+/// Type alias for the shmem GEM object type for this driver.
+pub(crate) type Object = shmem::Object<DriverObject>;
+
+impl gem::BaseDriverObject<Object> for DriverObject {
+ /// Callback to create the inner data of a GEM object
+ fn new(_dev: &VgemDevice, _size: usize) -> Result<Self> {
+ Ok(Self {})
+ }
+
+ /// Callback to drop all mappings for a GEM object owned by a given `File`
+ fn close(_obj: &Object, _file: &DrmFile) {}
+}
+
+impl shmem::DriverObject for DriverObject {
+ type Driver = VgemDriver;
+
+ const MAP_WC: bool = true;
+}
diff --git a/drivers/gpu/drm/rustgem/vgem.rs b/drivers/gpu/drm/rustgem/vgem.rs
new file mode 100644
index 000000000000..c2fc55bb39bd
--- /dev/null
+++ b/drivers/gpu/drm/rustgem/vgem.rs
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: MIT
+
+//! Driver for a Virtual GEM service.
+
+use kernel::driver::DeviceRemoval;
+use kernel::macros::vtable;
+use kernel::{
+ c_str, device, drm,
+ drm::{drv, ioctl},
+ error::Result,
+ platform,
+ prelude::*,
+ sync::Arc,
+};
+
+mod fence;
+mod file;
+mod gem;
+
+/// Driver metadata
+const INFO: drv::DriverInfo = drv::DriverInfo {
+ major: 1,
+ minor: 0,
+ patchlevel: 0,
+ name: c_str!("vgem"),
+ desc: c_str!("Virtual GEM provider"),
+ date: c_str!("20230201"),
+};
+
+struct Vgem {
+ data: Arc<DeviceData>,
+ _resource: device::Resource,
+ _pdev: platform::Device,
+}
+
+/// Empty struct representing this driver.
+pub(crate) struct VgemDriver;
+
+/// Convenience type alias for the DRM device type for this driver.
+pub(crate) type VgemDevice = kernel::drm::device::Device<VgemDriver>;
+
+///// Convenience type alias for the `device::Data` type for this driver.
+type DeviceData = device::Data<drv::Registration<VgemDriver>, (), ()>;
+
+#[vtable]
+impl drv::Driver for VgemDriver {
+ /// Our `DeviceData` type, reference-counted
+ type Data = Arc<DeviceData>;
+ /// Our `File` type.
+ type File = file::File;
+ /// Our `Object` type.
+ type Object = gem::Object;
+
+ const INFO: drv::DriverInfo = INFO;
+ const FEATURES: u32 = drv::FEAT_GEM | drv::FEAT_RENDER;
+
+ kernel::declare_drm_ioctls! {
+ (VGEM_FENCE_ATTACH, drm_vgem_fence_attach, ioctl::RENDER_ALLOW, file::File::attach),
+ (VGEM_FENCE_SIGNAL, drm_vgem_fence_signal, ioctl::RENDER_ALLOW, file::File::signal),
+ }
+}
+
+impl kernel::Module for Vgem {
+ fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
+ let mut pdev = platform::Device::register(c_str!("vgem"), -1)?;
+ let dev = device::Device::from_dev(&pdev);
+
+ let resource = dev.open_group(core::ptr::null_mut() as *mut core::ffi::c_void)?;
+
+ pdev.coerse_dma_masks(u64::MAX)?;
+
+ let reg = drm::drv::Registration::<VgemDriver>::new(&dev)?;
+
+ let data = kernel::new_device_data!(reg, (), (), "Vgem::Registrations")?;
+
+ let data = Arc::<DeviceData>::from(data);
+
+ kernel::drm_device_register!(
+ data.registrations().ok_or(ENXIO)?.as_pinned_mut(),
+ data.clone(),
+ 0
+ )?;
+
+ Ok(Vgem {
+ _pdev: pdev,
+ _resource: resource,
+ data,
+ })
+ }
+}
+
+impl Drop for Vgem {
+ fn drop(&mut self) {
+ self.data.device_remove();
+ }
+}
+
+module! {
+ type: Vgem,
+ name: "vgem",
+ author: "Maíra Canal",
+ description: "Virtual GEM provider",
+ license: "GPL",
+}
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1bf6d762d36e..dc44d3c3b9ad 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -37,6 +37,7 @@
#include <linux/xarray.h>
#include <uapi/asm-generic/ioctl.h>
#include <uapi/drm/drm.h>
+#include <uapi/drm/vgem_drm.h>
/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
--
2.39.2
More information about the dri-devel
mailing list