// 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 { try_pin_init!(Self { flags: args.flags }) } } /// Type alias for Tyr GEM buffer objects. pub(crate) type Bo = gem::shmem::Object; /// Creates a dummy GEM object to serve as the root of a GPUVM. pub(crate) fn new_dummy_object(ddev: &TyrDrmDevice) -> Result> { 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, /// The GPU VM this buffer is mapped into. vm: Arc>, /// The GPU VA range occupied by this buffer. va_range: Range, } 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>, size: u64, va_alloc: KernelBoVaAlloc, flags: VmMapFlags, ) -> Result { 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 ); } } }