From: Deborah Brouwer <deborah.brouwer@collabora.com>
To: Ke Sun <sunke@kylinos.cn>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Lorenzo Stoakes" <ljs@kernel.org>,
"Lyude Paul" <lyude@redhat.com>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
"Sami Tolvanen" <samitolvanen@google.com>,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org,
driver-core@lists.linux.dev, "Alvin Sun" <alvin.sun@linux.dev>
Subject: Re: [PATCH v5 07/11] drm/tyr: add VM-related ioctls
Date: Wed, 28 Oct 2026 12:41:31 -0700 [thread overview]
Message-ID: <auJP652IqlO6sAsF@um790> (raw)
In-Reply-To: <20261027-tyr-ioctls-v5-7-59dfe98cb003@kylinos.cn>
On Tue, Oct 27, 2026 at 12:41:00AM +0800, Ke Sun wrote:
> From: Alvin Sun <alvin.sun@linux.dev>
>
> Implement VM_CREATE, VM_DESTROY, VM_BIND and VM_GET_STATE. VM_CREATE
> hands the new VM to the per-file pool and, on insertion failure, the
> caller tears it down, so an aborted creation never leaks a VM.
> VM_DESTROY removes the VM from the pool and kills it, even if in-flight
> jobs still hold references (matching panthor: such jobs are expected
> to fault). VM_BIND runs synchronously for now.
>
> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
> ---
> drivers/gpu/drm/tyr/driver.rs | 12 +-
> drivers/gpu/drm/tyr/file.rs | 320 ++++++++++++++++++++++++++++++++++++++++--
> drivers/gpu/drm/tyr/gem.rs | 1 -
> drivers/gpu/drm/tyr/vm.rs | 69 +++++++--
> 4 files changed, 381 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
> index e2b6d63491b91..c0f17bc4ce2c9 100644
> --- a/drivers/gpu/drm/tyr/driver.rs
> +++ b/drivers/gpu/drm/tyr/driver.rs
> @@ -33,7 +33,7 @@
> Mutex, //
> },
> time,
> - types::CovariantForLt, //
> + types::ForLt, //
> };
>
> use crate::{
> @@ -74,6 +74,9 @@ pub(crate) struct TyrDrmRegistrationData<'drm> {
> /// Firmware sections.
> pub(crate) fw: Firmware<'drm>,
>
> + /// Memory management unit for address space slots.
> + pub(crate) mmu: Arc<Mmu<'drm>>,
> +
> #[pin]
> clks: Mutex<Clocks>,
>
> @@ -172,6 +175,7 @@ fn probe<'bound>(
> pin_init!(TyrDrmRegistrationData {
> pdev,
> fw: firmware,
> + mmu,
> clks <- new_mutex!(Clocks {
> core: core_clk,
> stacks: stacks_clk,
> @@ -210,7 +214,7 @@ fn drop(self: Pin<&mut Self>) {}
> impl drm::Driver for TyrDrmDriver {
> type Data = ();
> type RegistrationData<'drm> = TyrDrmRegistrationData<'drm>;
> - type File = CovariantForLt!(TyrDrmFileData);
> + type File = ForLt!(TyrDrmFileData<'_>);
> type Object = Bo;
> type ParentDevice<Ctx: DeviceContext> = platform::Device<Ctx>;
>
> @@ -219,6 +223,10 @@ impl drm::Driver for TyrDrmDriver {
>
> kernel::declare_drm_ioctls! {
> (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query),
> + (PANTHOR_VM_CREATE, drm_panthor_vm_create, ioctl::RENDER_ALLOW, TyrDrmFileData::vm_create),
> + (PANTHOR_VM_DESTROY, drm_panthor_vm_destroy, ioctl::RENDER_ALLOW, TyrDrmFileData::vm_destroy),
> + (PANTHOR_VM_BIND, drm_panthor_vm_bind, ioctl::RENDER_ALLOW, TyrDrmFileData::vm_bind),
> + (PANTHOR_VM_GET_STATE, drm_panthor_vm_get_state, ioctl::RENDER_ALLOW, TyrDrmFileData::vm_get_state),
> }
> }
>
> diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs
> index 933a365cb016e..22a74b4abf3de 100644
> --- a/drivers/gpu/drm/tyr/file.rs
> +++ b/drivers/gpu/drm/tyr/file.rs
> @@ -1,39 +1,64 @@
> // SPDX-License-Identifier: GPL-2.0 or MIT
>
> +use core::mem::offset_of;
> +
> use kernel::{
> drm::{
> self,
> + gem::BaseObject,
> Registered, //
> },
> prelude::*,
> - uaccess::UserSlice,
> + sizes::SizeConstants,
> + transmute::FromBytes,
> + uaccess::{
> + UserSlice,
> + UserSliceReader, //
> + },
> uapi, //
> };
>
> -use crate::driver::{
> - TyrDrmDevice,
> - TyrDrmDriver,
> - TyrDrmRegistrationData, //
> +use crate::{
> + driver::{
> + TyrDrmDevice,
> + TyrDrmDriver,
> + TyrDrmRegistrationData, //
> + },
> + vm::{
> + UserVaRequest,
> + Vm,
> + VmBindOpType,
> + VmMapFlags,
> + VmPool, //
> + }, //
> };
>
> #[pin_data]
> -pub(crate) struct TyrDrmFileData {}
> +pub(crate) struct TyrDrmFileData<'a> {
> + reg: &'a TyrDrmRegistrationData<'a>,
> +
> + #[pin]
> + vm_pool: VmPool<'a>,
> +}
>
> /// Convenience type alias for our DRM `File` type.
> pub(crate) type TyrDrmFile = drm::file::File<TyrDrmDriver>;
>
> -impl drm::file::DriverFile<'_> for TyrDrmFileData {
> +impl<'a> drm::file::DriverFile<'a> for TyrDrmFileData<'a> {
> type Driver = TyrDrmDriver;
>
> fn open(
> _device: &TyrDrmDevice<Registered>,
> - _reg_data: &TyrDrmRegistrationData<'_>,
> + reg_data: &'a TyrDrmRegistrationData<'a>,
> ) -> impl PinInit<Self, Error> {
> - Ok(Self {})
> + try_pin_init!(Self {
> + reg: reg_data,
> + vm_pool <- VmPool::new(reg_data.pdev.as_ref()),
> + })
> }
> }
>
> -impl TyrDrmFileData {
> +impl TyrDrmFileData<'_> {
> pub(crate) fn dev_query(
> _ddev: &TyrDrmDevice<Registered>,
> reg_data: &TyrDrmRegistrationData<'_>,
> @@ -65,4 +90,279 @@ pub(crate) fn dev_query(
> }
> }
> }
> +
> + pub(crate) fn vm_create(
> + ddev: &TyrDrmDevice<Registered>,
> + _reg_data: &TyrDrmRegistrationData<'_>,
> + vmcreate: &mut uapi::drm_panthor_vm_create,
> + file: &TyrDrmFile,
> + ) -> Result<u32> {
> + if vmcreate.flags != 0 {
> + dev_dbg!(
> + ddev.as_ref(),
> + "Invalid VM create flags: {:#x}\n",
> + vmcreate.flags
> + );
> + return Err(EINVAL);
> + }
> +
> + let ret: Result<u32, Error> = file.inner_with(|pfile| {
> + let vm = Vm::new_for_user(
> + pfile.reg.pdev.as_ref(),
> + ddev,
> + pfile.reg.mmu.as_arc_borrow(),
> + &pfile.reg.gpu_info,
> + UserVaRequest::from_uapi(vmcreate.user_va_range),
> + )?;
> + let user_va_range = vm.layout.user.end;
> + let id = pfile.vm_pool.add(vm).map_err(|e| {
> + e.vm.kill();
> + e.error
> + })?;
> + vmcreate.user_va_range = user_va_range;
> + vmcreate.id = id;
> +
> + Ok(0)
> + });
> + ret
> + }
> +
> + pub(crate) fn vm_destroy(
> + ddev: &TyrDrmDevice<Registered>,
> + _reg_data: &TyrDrmRegistrationData<'_>,
> + vmdestroy: &mut uapi::drm_panthor_vm_destroy,
> + file: &TyrDrmFile,
> + ) -> Result<u32> {
> + if vmdestroy.pad != 0 {
> + dev_dbg!(
> + ddev.as_ref(),
> + "Invalid VM destroy pad: {:#x}\n",
> + vmdestroy.pad
> + );
> + return Err(EINVAL);
> + }
> +
> + let ret: Result<u32, Error> = file.inner_with(|pfile| {
> + pfile.vm_pool.remove(vmdestroy.id)?;
> + Ok(0)
> + });
> + ret
> + }
> +
> + pub(crate) fn vm_bind(
> + ddev: &TyrDrmDevice<Registered>,
> + _reg_data: &TyrDrmRegistrationData<'_>,
> + vmbind: &mut uapi::drm_panthor_vm_bind,
> + file: &TyrDrmFile,
> + ) -> Result<u32> {
> + let async_flag = uapi::drm_panthor_vm_bind_flags_DRM_PANTHOR_VM_BIND_ASYNC;
> +
> + if vmbind.flags & !async_flag != 0 {
> + dev_dbg!(
> + ddev.as_ref(),
> + "Invalid VM_BIND flags: {:#x}\n",
> + vmbind.flags
> + );
> + return Err(EINVAL);
> + }
> +
> + if vmbind.flags & async_flag != 0 {
> + dev_dbg!(ddev.as_ref(), "Async VM_BIND not supported\n");
> + return Err(EOPNOTSUPP);
> + }
> +
> + let count = vmbind.ops.count as usize;
Since count is u32, we could use "usize::from_safe_cast"
> + if count == 0 {
In panthor_ioctl_vm_bind_sync(), the validity of the vm is checked first
before the ops array count, so if we get a scenario where the vm id
is invalid, but the count is 0, Tyr will return Ok but Panthor will
return EINVAL. Pehaps you could add here a quick check of the vm like:
+ return file.inner_with(|pfile| {
+ pfile.vm_pool.get(vmbind.vm_id).ok_or(EINVAL)?;
+ Ok(0)
+ });
> + return Ok(0);
> + }
> +
> + // Accept any stride that covers the trailing `syncs` array
> + // descriptor, so userspace built against older headers works.
> + static_assert!(
> + size_of::<VmBindOp>()
> + == offset_of!(uapi::drm_panthor_vm_bind_op, syncs)
> + + size_of::<uapi::drm_panthor_obj_array>()
> + );
> + let min_size = offset_of!(uapi::drm_panthor_vm_bind_op, syncs)
> + + size_of::<uapi::drm_panthor_obj_array>();
> + if min_size > vmbind.ops.stride as usize {
> + dev_dbg!(
> + ddev.as_ref(),
> + "Invalid VM_BIND op stride {} (expected at least {})\n",
> + vmbind.ops.stride,
> + size_of::<VmBindOp>()
> + );
> + return Err(EINVAL);
> + }
I think you should remove the static_assert, the min_size logic, and
the read_padding_zero() function here and for now just return EINVAL
if stride != size_of::<VmBindOp>
Since UserSliceReader.read::<VmBindOp> expects the exact size, I don't
think we can support variable strides yet.
Maybe add a comment like:
// TODO: support variable strides, including a minimum size,
// with copy_struct_from_user() semantics, including zero-padding
// shorter structs and validating that unknown trailing bytes are zero.
> + let stride = vmbind.ops.stride as usize;
> +
> + let total_len = stride.checked_mul(count).ok_or_else(|| {
> + dev_dbg!(ddev.as_ref(), "VM_BIND ops length overflow\n");
> + EINVAL
> + })?;
> + let mut reader =
> + UserSlice::new(UserPtr::from_addr(vmbind.ops.array as usize), total_len).reader();
> + let mut ops = KVec::new();
> + for _ in 0..count {
> + ops.push(reader.read::<VmBindOp>()?, GFP_KERNEL)?;
> + read_padding_zero(&mut reader, stride - size_of::<VmBindOp>())?;
> + }
> +
> + let ret: Result<u32, Error> = file.inner_with(|pfile| {
> + let vm = pfile.vm_pool.get(vmbind.vm_id).ok_or_else(|| {
> + dev_dbg!(ddev.as_ref(), "Invalid VM_BIND vm_id: {}\n", vmbind.vm_id);
> + EINVAL
> + })?;
> +
> + for (i, op) in ops.iter().enumerate() {
> + if let Err(e) = vm_bind_exec_op(&vm, file, op) {
> + dev_dbg!(ddev.as_ref(), "VM_BIND op {} failed: {:?}\n", i, e);
> + vmbind.ops.count = i as u32;
> + return Err(e);
> + }
> + }
> +
> + Ok(0)
> + });
> + ret
> + }
> +
> + pub(crate) fn vm_get_state(
> + ddev: &TyrDrmDevice<Registered>,
> + _reg_data: &TyrDrmRegistrationData<'_>,
> + vmgetstate: &mut uapi::drm_panthor_vm_get_state,
> + file: &TyrDrmFile,
> + ) -> Result<u32> {
> + file.inner_with(|pfile| {
> + let vm = pfile.vm_pool.get(vmgetstate.vm_id).ok_or_else(|| {
> + dev_dbg!(
> + ddev.as_ref(),
> + "Invalid VM_GET_STATE vm_id: {}\n",
> + vmgetstate.vm_id
> + );
> + EINVAL
> + })?;
> + vmgetstate.state = if vm.is_unusable() {
> + uapi::drm_panthor_vm_state_DRM_PANTHOR_VM_STATE_UNUSABLE
> + } else {
> + uapi::drm_panthor_vm_state_DRM_PANTHOR_VM_STATE_USABLE
> + };
> + Ok(0)
> + })
> + }
> +}
> +
> +fn vm_bind_exec_op(vm: &Vm<'_>, file: &TyrDrmFile, op: &VmBindOp) -> Result {
> + if op.size == 0 {
> + return Ok(());
> + }
> +
> + if op.syncs.count != 0 {
> + dev_dbg!(vm.dev(), "VM_BIND op syncs not supported\n");
> + return Err(EINVAL);
> + }
Panthor checks the op.syncs.count first before the op.size, so if we
get a corner-case where op has a size of 0 but a sync count > 0, this
would lead to Tyr returning Ok where Panthor would return EINVAL,
so might as well re-order the checks so we have parity.
> +
> + let end = match op.va.checked_add(op.size) {
> + Some(end) => end,
> + None => {
> + dev_dbg!(vm.dev(), "VM_BIND op VA range overflow\n");
> + return Err(EINVAL);
> + }
> + };
> + if op.va < vm.layout.user.start || end > vm.layout.user.end {
> + dev_dbg!(
> + vm.dev(),
> + "VM_BIND op VA range {:#x}..{:#x} outside user range\n",
> + op.va,
> + end
> + );
> + return Err(EINVAL);
> + }
> +
> + if (op.va | op.size | op.bo_offset) & (u64::SZ_4K - 1) != 0 {
> + dev_dbg!(vm.dev(), "VM_BIND op not GPU-page-aligned\n");
> + return Err(EINVAL);
> + }
> +
> + match VmBindOpType::try_from(op.flags) {
> + Ok(VmBindOpType::Map) => {
> + // Once the VM is unusable only MAP ops are rejected; UNMAP
> + // stays available for cleanup (see the UAPI docs).
> + if vm.is_unusable() {
> + dev_err!(vm.dev(), "VM_BIND map op on unusable VM\n");
Since this is something that userspace could trigger, should it be
dev_dbg!
> + return Err(EINVAL);
> + }
> +
> + let map_flags = match VmMapFlags::try_from(op.flags & !VmBindOpType::MASK) {
> + Ok(flags) => flags,
> + Err(_) => {
> + dev_dbg!(vm.dev(), "VM_BIND op invalid map flags {:#x}\n", op.flags);
> + return Err(EINVAL);
> + }
> + };
> + let bo = crate::gem::lookup_handle(file, op.bo_handle).map_err(|_| {
> + dev_dbg!(vm.dev(), "VM_BIND op invalid BO handle {}\n", op.bo_handle);
> + EINVAL
> + })?;
> + // Validate the BO window before mapping.
> + let bo_size = bo.size() as u64;
Since bo.size() returns a usize, you can use "u64::from_safe_cast(bo.size())
> + if op.size > bo_size || op.bo_offset > bo_size - op.size {
> + dev_dbg!(vm.dev(), "VM_BIND op BO range out of bounds\n");
> + return Err(EINVAL);
> + }
> + vm.map_bo_range(&bo, op.bo_offset, op.size, op.va, map_flags)
> + }
> + Ok(VmBindOpType::Unmap) => {
> + // Unmap must not carry map-specific flags or BO references.
> + if op.flags & !VmBindOpType::MASK != 0 || op.bo_handle != 0 || op.bo_offset != 0 {
> + dev_dbg!(
> + vm.dev(),
> + "VM_BIND UNMAP carries flags/BO refs: flags={:#x} bo_handle={} bo_offset={}\n",
> + op.flags,
> + op.bo_handle,
> + op.bo_offset
> + );
> + return Err(EINVAL);
> + }
> + vm.unmap_range(op.va, op.size)
> + }
> + Err(_) => {
> + dev_dbg!(
> + vm.dev(),
> + "VM_BIND op type {:#x} not supported\n",
> + op.flags & VmBindOpType::MASK
> + );
> + Err(EINVAL)
> + }
> + }
> +}
> +
> +/// Reads `len` bytes of array padding, rejecting any nonzero byte with `E2BIG`.
> +fn read_padding_zero(reader: &mut UserSliceReader, len: usize) -> Result {
> + let mut buf = [0u8; 64];
> + let mut remaining = len;
> + while remaining > 0 {
> + let chunk = remaining.min(buf.len());
> + reader.read_slice(&mut buf[..chunk])?;
> + if buf[..chunk].iter().any(|&b| b != 0) {
> + return Err(E2BIG);
> + }
> + remaining -= chunk;
> + }
> + Ok(())
> }
> +
> +#[repr(transparent)]
> +struct VmBindOp(uapi::drm_panthor_vm_bind_op);
> +
> +impl core::ops::Deref for VmBindOp {
> + type Target = uapi::drm_panthor_vm_bind_op;
> +
> + fn deref(&self) -> &Self::Target {
> + &self.0
> + }
> +}
> +
> +// SAFETY: `VmBindOp` contains only integers, so any bit pattern is valid;
> +// the `#[repr(transparent)]` wrapper has the same layout as the UAPI struct.
> +unsafe impl FromBytes for VmBindOp {}
Could you add a TODO: use zerocopy FromBytes when these traits are
derived for bindings. We have a series in the works for this, but I
don't want to add any more dependencies to your series.
> diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
> index 0e0989a48678f..60f6363995a5a 100644
> --- a/drivers/gpu/drm/tyr/gem.rs
> +++ b/drivers/gpu/drm/tyr/gem.rs
> @@ -77,7 +77,6 @@ pub(crate) fn new_object(ddev: &TyrDrmDevice, size: usize, flags: u32) -> Result
> }
>
> /// Look up a GEM object by handle for a DRM file.
> -#[expect(dead_code)]
> pub(crate) fn lookup_handle(file: &TyrDrmFile, handle: u32) -> Result<ARef<Bo>> {
> Bo::lookup_handle(file, handle)
> }
> diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs
> index b2fcda1e61073..5c3be79b2c595 100644
> --- a/drivers/gpu/drm/tyr/vm.rs
> +++ b/drivers/gpu/drm/tyr/vm.rs
> @@ -54,6 +54,11 @@
> },
> sync::{
> aref::ARef,
> + atomic::{
> + Acquire,
> + Atomic,
> + Release, //
> + },
> Arc,
> ArcBorrow,
> Mutex, //
> @@ -173,7 +178,6 @@ pub(crate) enum UserVaRequest {
>
> impl UserVaRequest {
> /// UAPI boundary normalization: `0` -> [`Auto`](Self::Auto).
> - #[expect(dead_code)]
> pub(crate) fn from_uapi(v: u64) -> Self {
> match NonZeroU64::new(v) {
> Some(size) => Self::Fixed(size),
> @@ -182,6 +186,38 @@ pub(crate) fn from_uapi(v: u64) -> Self {
> }
> }
>
> +/// Operation type, packed into the top nibble of
> +/// `drm_panthor_vm_bind_op::flags`.
> +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
> +pub(crate) enum VmBindOpType {
> + /// Map a BO range into the VM.
> + Map,
> + /// Unmap a VA range.
> + Unmap,
> +}
> +
> +impl VmBindOpType {
> + /// Bits occupied by the op type in `drm_panthor_vm_bind_op::flags`.
> + pub(crate) const MASK: u32 =
> + uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_OP_TYPE_MASK as u32;
> +}
> +
> +impl TryFrom<u32> for VmBindOpType {
> + type Error = Error;
> +
> + fn try_from(flags: u32) -> Result<Self, Self::Error> {
> + const MAP: u32 = uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_OP_TYPE_MAP as u32;
> + const UNMAP: u32 =
> + uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_OP_TYPE_UNMAP as u32;
> +
> + match flags & Self::MASK {
> + MAP => Ok(Self::Map),
> + UNMAP => Ok(Self::Unmap),
> + _ => Err(EINVAL),
> + }
> + }
> +}
> +
> /// Final user/kernel VA layout for a VM.
> pub(crate) struct VmLayout {
> /// Full GPU VA range covered by this VM.
> @@ -424,6 +460,8 @@ pub(crate) struct Vm<'drm> {
> gpuvm: ARef<GpuVm<GpuVmData<'drm>>>,
> /// VA layout for this VM.
> pub(crate) layout: VmLayout,
> + /// Whether the VM is unusable.
> + unusable: Atomic<bool>,
> }
>
> impl<'drm> Vm<'drm> {
> @@ -445,7 +483,6 @@ pub(crate) fn new_for_fw(
> }
>
> /// Creates a user VM, splitting the GPU VA range per `user_va`.
> - #[expect(dead_code)]
> pub(crate) fn new_for_user(
> dev: &'drm Device<Bound>,
> ddev: &TyrDrmDevice,
> @@ -512,6 +549,7 @@ fn new_internal(
> gpuvm,
> gpuvm_unique <- new_mutex!(gpuvm_unique),
> layout,
> + unusable: Atomic::new(false),
> }),
> GFP_KERNEL,
> )?;
> @@ -542,6 +580,7 @@ fn deactivate(&self) -> Result {
>
> /// Kills the VM by deactivating it and unmapping all regions.
> pub(crate) fn kill(&self) {
> + self.mark_unusable();
> let _ = self.deactivate();
> let _ = self
> .unmap_range(
> @@ -553,6 +592,15 @@ pub(crate) fn kill(&self) {
> });
> }
>
> + /// Marks the VM unusable.
> + fn mark_unusable(&self) {
> + self.unusable.store(true, Release);
> + }
> +
> + pub(crate) fn is_unusable(&self) -> bool {
> + self.unusable.load(Acquire)
> + }
> +
> /// Executes a virtual memory operation.
> ///
> /// This handles both map and unmap operations by coordinating between the
> @@ -664,6 +712,17 @@ pub(crate) fn map_bo_range(
> };
> let result = {
> let mut gpuvm_unique = self.gpuvm_unique.lock();
> + // Check under the GPUVM lock, which the kill path also takes to
> + // unmap, so this serializes against a concurrent teardown.
> + if self.is_unusable() {
> + dev_err!(
> + self.dev,
> + "Failed to map VA {:#x}..{:#x}: VM is unusable\n",
> + req.region.start,
> + req.region.end
> + );
> + return Err(EINVAL);
> + }
> self.exec_op(gpuvm_unique.as_mut().get_mut(), req, &mut resources)
> };
> // We flush the defer cleanup list now. Things will be different in
> @@ -1100,15 +1159,12 @@ pub(crate) struct VmPool<'drm> {
> /// Error returned when a VM cannot be added to the pool. The caller
> /// owns the teardown of the VM in [`AddError::vm`].
> pub(crate) struct AddError<'drm> {
> - #[expect(dead_code)]
> pub(crate) error: Error,
> - #[expect(dead_code)]
> pub(crate) vm: Arc<Vm<'drm>>,
> }
>
> impl<'drm> VmPool<'drm> {
> /// Creates a new [`VmPool`].
> - #[expect(dead_code)]
> pub(crate) fn new(dev: &'drm Device<Bound>) -> impl PinInit<Self> {
> let ids = IdPool::new();
> pin_init!(Self {
> @@ -1124,7 +1180,6 @@ pub(crate) fn new(dev: &'drm Device<Bound>) -> impl PinInit<Self> {
> /// returned in an [`AddError`] so the caller owns the teardown.
> // TODO: allocate IDs with the XArray directly (once it grows range
> // allocation, the equivalent of C's `XA_LIMIT`) and drop the IdPool.
> - #[expect(dead_code)]
> pub(crate) fn add(&self, vm: Arc<Vm<'drm>>) -> Result<u32, AddError<'drm>> {
> let id = (|| {
> let mut ids = self.ids.lock();
> @@ -1174,7 +1229,6 @@ pub(crate) fn add(&self, vm: Arc<Vm<'drm>>) -> Result<u32, AddError<'drm>> {
> }
>
> /// Removes and kills the VM with the given ID.
> - #[expect(dead_code)]
> pub(crate) fn remove(&self, id: u32) -> Result<()> {
> let mut vms = self.vms.lock();
> let vm = match vms.remove(usize::from_safe_cast(id)) {
> @@ -1191,7 +1245,6 @@ pub(crate) fn remove(&self, id: u32) -> Result<()> {
> }
>
> /// Gets a shared reference to the VM with the given ID.
> - #[expect(dead_code)]
> pub(crate) fn get(&self, id: u32) -> Option<Arc<Vm<'drm>>> {
> let vms = self.vms.lock();
> let borrow = vms.get(usize::from_safe_cast(id))?;
>
> --
> 2.43.0
>
next prev parent reply other threads:[~2026-10-28 19:42 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 16:40 [PATCH v5 00/11] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
2026-10-26 16:40 ` [PATCH v5 01/11] rust: sizes: add SZ_4G constant Ke Sun via B4 Relay
2026-10-26 16:40 ` [PATCH v5 02/11] rust: task: add task_size() Ke Sun via B4 Relay
2026-10-26 16:40 ` [PATCH v5 03/11] rust: sync: arc: relax `ForeignOwnable` for `Arc<T>` Ke Sun via B4 Relay
2026-10-26 16:40 ` [PATCH v5 04/11] drm/tyr: add per-file VM pool Ke Sun via B4 Relay
2026-10-26 16:40 ` [PATCH v5 05/11] drm/tyr: add user and MCU VM specifications Ke Sun via B4 Relay
2026-10-28 0:23 ` Deborah Brouwer
2026-10-26 16:40 ` [PATCH v5 06/11] drm/tyr: add BO creation and lookup helpers Ke Sun via B4 Relay
2026-10-26 16:41 ` [PATCH v5 07/11] drm/tyr: add VM-related ioctls Ke Sun via B4 Relay
2026-10-28 19:41 ` Deborah Brouwer [this message]
2026-10-26 16:41 ` [PATCH v5 08/11] drm/tyr: add BO-related ioctls Ke Sun via B4 Relay
2026-10-29 0:29 ` Deborah Brouwer
2026-10-26 16:41 ` [PATCH v5 09/11] rust: device: expose the device DMA attribute Ke Sun via B4 Relay
2026-10-26 16:41 ` [PATCH v5 10/11] drm/tyr: gem: flush write-combine BOs from probe-time coherence Ke Sun via B4 Relay
2026-10-26 16:41 ` [PATCH v5 11/11] drm/tyr: gem: map cached BOs for DRM_PANTHOR_BO_WB_MMAP Ke Sun via B4 Relay
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=auJP652IqlO6sAsF@um790 \
--to=deborah.brouwer@collabora.com \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=alvin.sun@linux.dev \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=ljs@kernel.org \
--cc=lossin@kernel.org \
--cc=lyude@redhat.com \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=samitolvanen@google.com \
--cc=simona@ffwll.ch \
--cc=sunke@kylinos.cn \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox