| // SPDX-License-Identifier: GPL-2.0 or MIT |
| //! GEM buffer object management for the Tyr driver. |
| //! |
| //! This module provides buffer object (BO) management functionality using |
| //! DRM's GEM subsystem with shmem backing. |
| |
| use core::ops::Range; |
| |
| use kernel::{ |
| drm::gem::{ |
| self, |
| shmem, // |
| }, |
| prelude::*, |
| sync::{ |
| aref::ARef, |
| Arc, // |
| }, // |
| }; |
| |
| use crate::{ |
| driver::{ |
| TyrDrmDevice, |
| TyrDrmDriver, // |
| }, |
| vm::{ |
| Vm, |
| VmMapFlags, // |
| }, |
| }; |
| |
| /// Tyr's DriverObject type for GEM objects. |
| #[pin_data] |
| pub(crate) struct BoData { |
| flags: u32, |
| } |
| |
| /// Provides a way to pass arguments when creating BoData |
| /// as required by the gem::DriverObject trait. |
| pub(crate) struct BoCreateArgs { |
| flags: u32, |
| } |
| |
| impl gem::DriverObject for BoData { |
| type Driver = TyrDrmDriver; |
| type Args = BoCreateArgs; |
| |
| fn new(_dev: &TyrDrmDevice, _size: usize, args: BoCreateArgs) -> impl PinInit<Self, Error> { |
| try_pin_init!(Self { flags: args.flags }) |
| } |
| } |
| |
| /// Type alias for Tyr GEM buffer objects. |
| pub(crate) type Bo = gem::shmem::Object<BoData>; |
| |
| /// Creates a dummy GEM object to serve as the root of a GPUVM. |
| pub(crate) fn new_dummy_object(ddev: &TyrDrmDevice) -> Result<ARef<Bo>> { |
| let bo = Bo::new( |
| ddev, |
| 4096, |
| shmem::ObjectConfig { |
| map_wc: true, |
| parent_resv_obj: None, |
| }, |
| BoCreateArgs { flags: 0 }, |
| )?; |
| |
| Ok(bo) |
| } |
| |
| /// Specifies how to choose a GPU virtual address for a [`KernelBo`]. |
| /// An automatic VA allocation strategy will be added in the future. |
| pub(crate) enum KernelBoVaAlloc { |
| /// Explicit VA address specified by the caller. |
| Explicit(u64), |
| } |
| |
| /// A kernel-owned buffer object with automatic GPU virtual address mapping. |
| /// |
| /// This structure represents a buffer object that is created and managed entirely |
| /// by the kernel driver, as opposed to userspace-created GEM objects. It combines |
| /// a GEM object with automatic GPU virtual address (VA) space mapping and cleanup. |
| /// |
| /// When dropped, the buffer is automatically unmapped from the GPU VA space. |
| pub(crate) struct KernelBo<'drm> { |
| /// The underlying GEM buffer object. |
| bo: ARef<Bo>, |
| /// The GPU VM this buffer is mapped into. |
| vm: Arc<Vm<'drm>>, |
| /// The GPU VA range occupied by this buffer. |
| va_range: Range<u64>, |
| } |
| |
| impl<'drm> KernelBo<'drm> { |
| /// Creates a new kernel-owned buffer object and maps it into GPU VA space. |
| /// |
| /// This function allocates a new shmem-backed GEM object and immediately maps |
| /// it into the specified GPU virtual memory space. The mapping is automatically |
| /// cleaned up when the [`KernelBo`] is dropped. |
| pub(crate) fn new( |
| ddev: &TyrDrmDevice, |
| vm: Arc<Vm<'drm>>, |
| size: u64, |
| va_alloc: KernelBoVaAlloc, |
| flags: VmMapFlags, |
| ) -> Result<Self> { |
| if size == 0 { |
| dev_err!(vm.dev(), "Cannot create KernelBo with size 0"); |
| return Err(EINVAL); |
| } |
| |
| let KernelBoVaAlloc::Explicit(va) = va_alloc; |
| |
| let bo_size = usize::try_from(size).map_err(|_| EOVERFLOW)?; |
| let va_end = va.checked_add(size).ok_or(EINVAL)?; |
| |
| let bo = Bo::new( |
| ddev, |
| bo_size, |
| shmem::ObjectConfig { |
| map_wc: true, |
| parent_resv_obj: None, |
| }, |
| BoCreateArgs { flags: 0 }, |
| )?; |
| |
| vm.map_bo_range(&bo, 0, size, va, flags)?; |
| |
| Ok(KernelBo { |
| bo, |
| vm, |
| va_range: va..va_end, |
| }) |
| } |
| |
| pub(crate) fn bo(&self) -> &Bo { |
| &self.bo |
| } |
| } |
| |
| impl Drop for KernelBo<'_> { |
| fn drop(&mut self) { |
| let va = self.va_range.start; |
| let size = self.va_range.end - self.va_range.start; |
| |
| if let Err(e) = self.vm.unmap_range(va, size) { |
| // If unmap_range fails, it is still safe to drop the |
| // KernelBo and its ARef to the GEM buffer object because |
| // GPUVM also holds a reference to the GEM buffer object. |
| // The physical pages won't be freed or reallocated. |
| dev_err!( |
| self.vm.dev(), |
| "Failed to unmap KernelBo range {:#x}..{:#x}: {:?}", |
| self.va_range.start, |
| self.va_range.end, |
| e |
| ); |
| } |
| } |
| } |