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@xxxxxxxxxx> --- 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