All of lore.kernel.org
 help / color / mirror / Atom feed
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
> 

  reply	other threads:[~2026-10-28 19:41 UTC|newest]

Thread overview: 27+ 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 ` Ke Sun
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   ` Ke Sun
2026-10-26 16:40 ` [PATCH v5 02/11] rust: task: add task_size() Ke Sun via B4 Relay
2026-10-26 16:40   ` Ke Sun
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   ` Ke Sun
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   ` Ke Sun
2026-10-26 16:40 ` [PATCH v5 05/11] drm/tyr: add user and MCU VM specifications Ke Sun via B4 Relay
2026-10-26 16:40   ` Ke Sun
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:40   ` Ke Sun
2026-10-26 16:41 ` [PATCH v5 07/11] drm/tyr: add VM-related ioctls Ke Sun via B4 Relay
2026-10-26 16:41   ` Ke Sun
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-26 16:41   ` Ke Sun
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   ` Ke Sun
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   ` Ke Sun
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
2026-10-26 16:41   ` Ke Sun

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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.