429 lines · 14.9 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 OR MIT use super::*; /// The actual data that gets threaded through the callbacks.struct SmData<'a, 'ctx, T: DriverGpuVm + 'ctx> {    gpuvm: &'a mut UniqueRefGpuVm<T>,    user_context: &'a mut T::SmContext<'ctx>,} /// Adds an extra field to `SmData` for `sm_map()` callbacks.////// # Invariants////// `self.vm_bo.gpuvm() == self.sm_data.gpuvm`.#[repr(C)]struct SmMapData<'a, 'ctx, T: DriverGpuVm> {    sm_data: SmData<'a, 'ctx, T>,    vm_bo: &'a GpuVmBo<T>,} /// The argument for [`UniqueRefGpuVm::sm_map`].pub struct OpMapRequest<'a, 'ctx, T: DriverGpuVm + 'ctx> {    /// Address in GPU virtual address space.    pub addr: u64,    /// Length of mapping to create.    pub range: u64,    /// Offset in GEM object.    pub gem_offset: u64,    /// The GEM object to map.    pub vm_bo: &'a GpuVmBo<T>,    /// The user-provided context type.    pub context: &'a mut T::SmContext<'ctx>,} impl<'a, 'ctx, T: DriverGpuVm> OpMapRequest<'a, 'ctx, T> {    fn raw_request(&self) -> bindings::drm_gpuvm_map_req {        bindings::drm_gpuvm_map_req {            map: bindings::drm_gpuva_op_map {                va: bindings::drm_gpuva_op_map__bindgen_ty_1 {                    addr: self.addr,                    range: self.range,                },                gem: bindings::drm_gpuva_op_map__bindgen_ty_2 {                    offset: self.gem_offset,                    obj: self.vm_bo.obj().as_raw(),                },            },        }    }} /// Represents an `sm_step_map` operation that has not yet been completed.pub struct OpMap<'op, T: DriverGpuVm> {    op: &'op bindings::drm_gpuva_op_map,    // Since these abstractions are designed for immediate mode, the VM BO needs to be    // pre-allocated, so we always have it available when we reach this point.    vm_bo: &'op GpuVmBo<T>,    // This ensures that 'op is invariant, so that `OpMap<'long, T>` does not    // coerce to `OpMap<'short, T>`. This ensures that the user can't return    // the wrong `OpMapped` value.    _invariant: PhantomData<*mut &'op mut T>,} impl<'op, T: DriverGpuVm> OpMap<'op, T> {    /// The base address of the new mapping.    pub fn addr(&self) -> u64 {        self.op.va.addr    }     /// The length of the new mapping.    pub fn length(&self) -> u64 {        self.op.va.range    }     /// The offset within the [`drm_gem_object`](DriverGpuVm::Object).    pub fn gem_offset(&self) -> u64 {        self.op.gem.offset    }     /// The [`drm_gem_object`](DriverGpuVm::Object) to map.    pub fn obj(&self) -> &T::Object {        // SAFETY: The `obj` pointer is guaranteed to be valid.        unsafe { <T::Object as IntoGEMObject>::from_raw(self.op.gem.obj) }    }     /// The [`GpuVmBo`] that the new VA will be associated with.    pub fn vm_bo(&self) -> &GpuVmBo<T> {        self.vm_bo    }     /// Use the pre-allocated VA to carry out this map operation.    pub fn insert(self, va: GpuVaAlloc<T>, va_data: impl PinInit<T::VaData>) -> OpMapped<'op, T> {        let va = va.prepare(va_data);        // SAFETY: By the type invariants we may access the interval tree.        unsafe { bindings::drm_gpuva_map(self.vm_bo.gpuvm().as_raw(), va, self.op) };         let _gpuva_guard = self.vm_bo().lock_gpuva();        // SAFETY: The va is prepared for insertion, and we hold the GEM lock.        unsafe { bindings::drm_gpuva_link(va, self.vm_bo.as_raw()) };         OpMapped {            _invariant: self._invariant,        }    }} /// Represents a completed [`OpMap`] operation.pub struct OpMapped<'op, T> {    _invariant: PhantomData<*mut &'op mut T>,} /// Represents an `sm_step_unmap` operation that has not yet been completed.pub struct OpUnmap<'op, T: DriverGpuVm> {    op: &'op bindings::drm_gpuva_op_unmap,    // This ensures that 'op is invariant, so that `OpUnmap<'long, T>` does not    // coerce to `OpUnmap<'short, T>`. This ensures that the user can't return the    // wrong`OpUnmapped` value.    _invariant: PhantomData<*mut &'op mut T>,} impl<'op, T: DriverGpuVm> OpUnmap<'op, T> {    /// Indicates whether this [`GpuVa`] is physically contiguous with the    /// original mapping request.    ///    /// Optionally, if `keep` is set, drivers may keep the actual page table    /// mappings for this `drm_gpuva`, adding the missing page table entries    /// only and update the `drm_gpuvm` accordingly.    pub fn keep(&self) -> bool {        self.op.keep    }     /// The range being unmapped.    pub fn va(&self) -> &GpuVa<T> {        // SAFETY: This is a valid va. It's not the `kernel_alloc_node` because you can't unmap it,        // and it's not sparse by the `GpuVm<T>` type invariants.        unsafe { GpuVa::<T>::from_raw(self.op.va) }    }     /// Remove the VA.    pub fn remove(self) -> (OpUnmapped<'op, T>, GpuVaRemoved<T>) {        // SAFETY: The op references a valid drm_gpuva in the GPUVM.        unsafe { bindings::drm_gpuva_unmap(self.op) };        // SAFETY: The va is no longer in the interval tree so we may unlink it.        unsafe { bindings::drm_gpuva_unlink_defer(self.op.va) };         // SAFETY: We just removed this va from the `GpuVm<T>`.        let va = unsafe { GpuVaRemoved::from_raw(self.op.va) };         (            OpUnmapped {                _invariant: self._invariant,            },            va,        )    }} /// Represents a completed [`OpUnmap`] operation.pub struct OpUnmapped<'op, T> {    _invariant: PhantomData<*mut &'op mut T>,} /// Represents an `sm_step_remap` operation that has not yet been completed.pub struct OpRemap<'op, T: DriverGpuVm> {    op: &'op bindings::drm_gpuva_op_remap,    // This ensures that 'op is invariant, so that `OpRemap<'long, T>` does not    // coerce to `OpRemap<'short, T>`. This ensures that the user can't return the    // wrong`OpRemapped` value.    _invariant: PhantomData<*mut &'op mut T>,} impl<'op, T: DriverGpuVm> OpRemap<'op, T> {    /// The preceding part of a split mapping.    #[inline]    pub fn prev(&self) -> Option<&OpRemapMapData> {        // SAFETY: We checked for null, so the pointer must be valid.        NonNull::new(self.op.prev).map(|ptr| unsafe { OpRemapMapData::from_raw(ptr) })    }     /// The subsequent part of a split mapping.    #[inline]    pub fn next(&self) -> Option<&OpRemapMapData> {        // SAFETY: We checked for null, so the pointer must be valid.        NonNull::new(self.op.next).map(|ptr| unsafe { OpRemapMapData::from_raw(ptr) })    }     /// Indicates whether the `drm_gpuva` being removed is physically contiguous with the original    /// mapping request.    ///    /// Optionally, if `keep` is set, drivers may keep the actual page table mappings for this    /// `drm_gpuva`, adding the missing page table entries only and update the `drm_gpuvm`    /// accordingly.    #[inline]    pub fn keep(&self) -> bool {        // SAFETY: The unmap pointer is always valid.        unsafe { (*self.op.unmap).keep }    }     /// The range being unmapped.    #[inline]    pub fn va_to_unmap(&self) -> &GpuVa<T> {        // SAFETY: This is a valid va. It's not the `kernel_alloc_node` because you can't unmap it,        // and it's not sparse by the `GpuVm<T>` type invariants.        unsafe { GpuVa::<T>::from_raw((*self.op.unmap).va) }    }     /// The [`drm_gem_object`](DriverGpuVm::Object) whose VA is being remapped.    #[inline]    pub fn obj(&self) -> &T::Object {        self.va_to_unmap().obj()    }     /// The [`GpuVmBo`] that is being remapped.    #[inline]    pub fn vm_bo(&self) -> &GpuVmBo<T> {        self.va_to_unmap().vm_bo()    }     /// Update the GPUVM to perform the remapping.    pub fn remap(        self,        va_alloc: [GpuVaAlloc<T>; 2],        prev_data: impl PinInit<T::VaData>,        next_data: impl PinInit<T::VaData>,    ) -> (OpRemapped<'op, T>, OpRemapRet<T>) {        let [va1, va2] = va_alloc;         let mut unused_va = None;        let mut prev_ptr = ptr::null_mut();        let mut next_ptr = ptr::null_mut();        if self.prev().is_some() {            prev_ptr = va1.prepare(prev_data);        } else {            unused_va = Some(va1);        }        if self.next().is_some() {            next_ptr = va2.prepare(next_data);        } else {            unused_va = Some(va2);        }         // SAFETY: the pointers are non-null when required        unsafe { bindings::drm_gpuva_remap(prev_ptr, next_ptr, self.op) };         let gpuva_guard = self.vm_bo().lock_gpuva();        if !prev_ptr.is_null() {            // SAFETY: The prev_ptr is a valid drm_gpuva prepared for insertion. The vm_bo is still            // valid as the not-yet-unlinked gpuva holds a refcount on the vm_bo.            unsafe { bindings::drm_gpuva_link(prev_ptr, self.vm_bo().as_raw()) };        }        if !next_ptr.is_null() {            // SAFETY: The next_ptr is a valid drm_gpuva prepared for insertion. The vm_bo is still            // valid as the not-yet-unlinked gpuva holds a refcount on the vm_bo.            unsafe { bindings::drm_gpuva_link(next_ptr, self.vm_bo().as_raw()) };        }        drop(gpuva_guard);         // SAFETY: The va is no longer in the interval tree so we may unlink it.        unsafe { bindings::drm_gpuva_unlink_defer((*self.op.unmap).va) };         (            OpRemapped {                _invariant: self._invariant,            },            OpRemapRet {                // SAFETY: We just removed this va from the `GpuVm<T>`.                unmapped_va: unsafe { GpuVaRemoved::from_raw((*self.op.unmap).va) },                unused_va,            },        )    }} /// Part of an [`OpRemap`] that represents a new mapping.#[repr(transparent)]pub struct OpRemapMapData(bindings::drm_gpuva_op_map); impl OpRemapMapData {    /// # Safety    /// Must reference a valid `drm_gpuva_op_map` for duration of `'a`.    unsafe fn from_raw<'a>(ptr: NonNull<bindings::drm_gpuva_op_map>) -> &'a Self {        // SAFETY: ok per safety requirements        unsafe { ptr.cast().as_ref() }    }     /// The base address of the new mapping.    pub fn addr(&self) -> u64 {        self.0.va.addr    }     /// The length of the new mapping.    pub fn length(&self) -> u64 {        self.0.va.range    }     /// The offset within the [`drm_gem_object`](DriverGpuVm::Object).    pub fn gem_offset(&self) -> u64 {        self.0.gem.offset    }} /// Struct containing objects removed or not used by [`OpRemap::remap`].pub struct OpRemapRet<T: DriverGpuVm> {    /// The `drm_gpuva` that was removed.    pub unmapped_va: GpuVaRemoved<T>,    /// If the remap did not split the region into two pieces, then the unused `drm_gpuva` is    /// returned here.    pub unused_va: Option<GpuVaAlloc<T>>,} /// Represents a completed [`OpRemap`] operation.pub struct OpRemapped<'op, T> {    _invariant: PhantomData<*mut &'op mut T>,} impl<T: DriverGpuVm> UniqueRefGpuVm<T> {    /// Create a mapping, removing or remapping anything that overlaps.    ///    /// Internally calls the [`DriverGpuVm`] callbacks similar to [`Self::sm_unmap`], except that    /// the [`DriverGpuVm::sm_step_map`] is called once to create the requested mapping.    #[inline]    pub fn sm_map(&mut self, req: OpMapRequest<'_, '_, T>) -> Result {        if req.vm_bo.gpuvm() != &**self {            return Err(EINVAL);        }         let gpuvm = self.as_raw();        let raw_req = req.raw_request();        // INVARIANT: Checked above that `vm_bo.gpuvm() == self`.        let mut p = SmMapData {            sm_data: SmData {                gpuvm: self,                user_context: req.context,            },            vm_bo: req.vm_bo,        };        // SAFETY:        // * raw_request() creates a valid request.        // * The private data is valid to be interpreted as both SmData and SmMapData since the        //   first field of SmMapData is SmData.        to_result(unsafe {            bindings::drm_gpuvm_sm_map(gpuvm, (&raw mut p).cast(), &raw const raw_req)        })    }     /// Remove any mappings in the given region.    ///    /// Internally calls [`DriverGpuVm::sm_step_unmap`] for ranges entirely contained within the    /// given range, and [`DriverGpuVm::sm_step_remap`] for ranges that overlap with the range.    #[inline]    pub fn sm_unmap(&mut self, addr: u64, length: u64, context: &mut T::SmContext<'_>) -> Result {        let gpuvm = self.as_raw();        let mut p = SmData {            gpuvm: self,            user_context: context,        };        // SAFETY:        // * raw_request() creates a valid request.        // * The private data is a valid SmData.        to_result(unsafe { bindings::drm_gpuvm_sm_unmap(gpuvm, (&raw mut p).cast(), addr, length) })    }} impl<T: DriverGpuVm> GpuVm<T> {    /// # Safety    /// Must be called from `sm_map` with a pointer to `SmMapData`.    pub(super) unsafe extern "C" fn sm_step_map(        op: *mut bindings::drm_gpuva_op,        p: *mut c_void,    ) -> c_int {        // SAFETY: If we reach `sm_step_map` then we were called from `sm_map` which always passes        // an `SmMapData` as private data.        let p = unsafe { &mut *p.cast::<SmMapData<'_, '_, T>>() };        let op = OpMap {            // SAFETY: sm_step_map is called with a map operation.            op: unsafe { &(*op).__bindgen_anon_1.map },            vm_bo: p.vm_bo,            _invariant: PhantomData,        };        match p            .sm_data            .gpuvm            .data()            .sm_step_map(op, p.sm_data.user_context)        {            Ok(OpMapped { .. }) => 0,            Err(err) => err.to_errno(),        }    }     /// # Safety    /// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.    pub(super) unsafe extern "C" fn sm_step_unmap(        op: *mut bindings::drm_gpuva_op,        p: *mut c_void,    ) -> c_int {        // SAFETY: The caller provides a pointer that can be treated as `SmData`.        let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };        let op = OpUnmap {            // SAFETY: sm_step_unmap is called with an unmap operation.            op: unsafe { &(*op).__bindgen_anon_1.unmap },            _invariant: PhantomData,        };        match p.gpuvm.data().sm_step_unmap(op, p.user_context) {            Ok(OpUnmapped { .. }) => 0,            Err(err) => err.to_errno(),        }    }     /// # Safety    /// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.    pub(super) unsafe extern "C" fn sm_step_remap(        op: *mut bindings::drm_gpuva_op,        p: *mut c_void,    ) -> c_int {        // SAFETY: The caller provides a pointer that can be treated as `SmData`.        let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };        let op = OpRemap {            // SAFETY: sm_step_remap is called with a remap operation.            op: unsafe { &(*op).__bindgen_anon_1.remap },            _invariant: PhantomData,        };        match p.gpuvm.data().sm_step_remap(op, p.user_context) {            Ok(OpRemapped { .. }) => 0,            Err(err) => err.to_errno(),        }    }}