* [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support
@ 2026-11-09 0:36 Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 01/10] rust: sizes: add SZ_4G constant Ke Sun via B4 Relay
` (9 more replies)
0 siblings, 10 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
Add the VM and BO ioctls to the Tyr driver, aligning its userspace
interface with panthor.
The series is based on v2 of Danilo's lifetime-parameterized DRM File
private data series:
https://lore.kernel.org/all/20261107021427.2105374-1-dakr@kernel.org/
It also carries Sami Tolvanen's buffer cache-management patches from
https://gitlab.freedesktop.org/panfrost/linux sami/tyr/7.3/dev.
The panthor GEM and VM IGT tests pass on an RK3588S device. The group
and dev_query tests are not supported yet.
A branch with the full series and all prerequisites applied is
available at:
https://gitlab.freedesktop.org/alvinsun039/linux.git tyr-ioctls
Link: https://gitlab.freedesktop.org/panfrost/linux/-/merge_requests/64
Signed-off-by: Ke Sun <sunke@kylinos.cn>
---
Changes in v6:
- Rebase onto v2 of Danilo's lifetime-parameterized DRM File private
data series and use the Driver::File<'a> associated type instead of
ForLt.
- Drop the unrelated Firmware::new closure rework from the VM
specifications patch (Deborah).
- Merge the write-combine and WB_MMAP patches into one, with the full
should_map_wc() helper in a single place (Deborah).
- Accept DRM_PANTHOR_BO_WB_MMAP directly in the BO_CREATE patch,
reject NO_MMAP combined with WB_MMAP, and return EOPNOTSUPP for
unsupported requests such as an exclusive VM (Deborah).
- Pass the bound device from file.inner_with() to new_object() and
drop the unsafe as_bound() access (Deborah).
- Expose all three DmaAttr values as constants instead of providing
is_coherent() (Deborah).
- Validate the VM id before the empty-ops early return in VM_BIND and
reject sync objects before the size check, as in Panthor (Deborah).
- Require an exact ops stride for now, use from_safe_cast for the id
and size conversions, and add TODO notes for variable strides and
zerocopy FromBytes (Deborah).
- Link to v5: https://patch.msgid.link/20261027-tyr-ioctls-v5-0-59dfe98cb003@kylinos.cn
Changes in v5:
- Replace the mm-based task_size() (always returned 0) with Sami's
CurrentTask::task_size() implementation (Deborah, Alice).
- Use from_safe_cast() for the id conversions and annotate the AddError
fields so the VM pool patch compiles on its own (Deborah).
- Store a clone on pool conflicts and return the caller's VM instead of
expect()-ing (Deborah).
- Drop the redundant project() in PinnedDrop (Deborah).
- Fix the rustfmt issues found in the series (Deborah).
- Link to v4: https://patch.msgid.link/20261020-tyr-ioctls-v4-0-704de77da223@kylinos.cn
Changes in v4:
- Rework VmPool teardown: add() returns an AddError the caller owns,
a conflicting stored VM is killed with EBUSY, and remove() kills
before the ID is reused (Daniel, Deborah).
- Wrap dev_dma_attr in a DmaAttr type with dma_attr()/is_coherent()
and rename the patch accordingly (Danilo).
- Fetch the sg_table unconditionally in new_object() and drop the
conditional flush call (Daniel).
- Use dev_dbg!() instead of dev_err!() for user-input validation and
EOPNOTSUPP for the async VM_BIND path (Daniel).
- Drop the pr_err!() calls from the VM specification patch (Daniel).
- Link to v3: https://patch.msgid.link/20260929-tyr-ioctls-v3-0-26955fb111d5@kylinos.cn
Changes in v3:
- Drop the VmOwner wrapper and kill VMs explicitly on destroy and
file close (Deborah).
- Add Sami Tolvanen's buffer cache-management patches: coherent
devices map buffers cached, write-combine buffers flush at
creation, and BO_CREATE accepts WB_MMAP.
- Drop the new_dummy_object refactor; the dummy object stays a direct
Bo::new since new_object now fetches the sgtable for write-combine
buffers.
- Document the LargeSizeConstants trait in the sizes patch (Miguel).
- Misc commit message and comment cleanups.
- Link to v2: https://lore.kernel.org/r/20260908-tyr-ioctls-v2-0-88bea777df67@kylinos.cn
Changes in v2:
- Fold the VM pool into vm.rs and cap it at 32 VMs per file (Deborah).
- Add VmOwner so a VM is killed exactly once on drop, regardless of
remaining references (Daniel).
- Replace the VmSpec enum with new_for_fw()/new_for_user()
constructors (Daniel).
- Parse VM_BIND op types via VmBindOpType and use the kernel
Atomic<bool> for the unusable flag (Daniel).
- Use PAGE_SIZE for the dummy object size (Daniel).
- Rename BoData::create_flags() to flags() (Daniel).
- Report VA-split failures with dev_err! at the caller.
- Link to v1: https://lore.kernel.org/r/20260902-tyr-ioctls-v1-0-e0fdbf8bd108@kylinos.cn
---
Alvin Sun (7):
rust: sizes: add SZ_4G constant
rust: sync: arc: relax `ForeignOwnable` for `Arc<T>`
drm/tyr: add per-file VM pool
drm/tyr: add user and MCU VM specifications
drm/tyr: add BO creation and lookup helpers
drm/tyr: add VM-related ioctls
drm/tyr: add BO-related ioctls
Sami Tolvanen (3):
rust: task: add task_size()
rust: device: expose the device DMA attribute
drm/tyr: support write-combine BO mappings
drivers/gpu/drm/tyr/driver.rs | 25 ++-
drivers/gpu/drm/tyr/file.rs | 390 +++++++++++++++++++++++++++++++++++++++-
drivers/gpu/drm/tyr/fw.rs | 5 +-
drivers/gpu/drm/tyr/gem.rs | 79 +++++++-
drivers/gpu/drm/tyr/vm.rs | 355 ++++++++++++++++++++++++++++++++++--
rust/bindings/bindings_helper.h | 1 +
rust/helpers/task.c | 6 +
rust/kernel/device.rs | 27 +++
rust/kernel/sizes.rs | 12 ++
rust/kernel/sync/arc.rs | 12 +-
rust/kernel/task.rs | 12 ++
11 files changed, 883 insertions(+), 41 deletions(-)
---
base-commit: cef0cd809e564cca9ac824a369c995ab7e0d16f3
change-id: 20260901-tyr-ioctls-f10ef0dcbfa6
prerequisite-message-id: <20261107021427.2105374-1-dakr@kernel.org>
prerequisite-patch-id: e17775a2fb002d71edd479f6002dc3380b9b6bb4
prerequisite-patch-id: 388c5b871c09dbb6bad46c68be04813bcbdaadb6
prerequisite-patch-id: f31098f7dcd1b2d7843767f9ef778788f8773477
prerequisite-patch-id: b0ea30b4c39e5a43e53ceaf28bd8b39db5b26a76
prerequisite-patch-id: e0735b7cd0f226e702c0edc4ed889340f93d24cf
prerequisite-patch-id: 086eb00ec9c9c445c99bbb8d1a052b3fad56b5bd
prerequisite-patch-id: d186dd1af2f3ce256afd8c7b6ff749d74def5d1a
Best regards,
--
Ke Sun <sunke@kylinos.cn>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v6 01/10] rust: sizes: add SZ_4G constant
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 02/10] rust: task: add task_size() Ke Sun via B4 Relay
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Alvin Sun <alvin.sun@linux.dev>
SZ_4G is used by Tyr when splitting the GPU VA range into
user and kernel regions. Add a LargeSizeConstants trait, implemented
for u64 since SizeConstants only covers sizes that fit in a u32.
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/sizes.rs | 12 ++++++++++++
2 files changed, 13 insertions(+)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 3791af0aadc73..fd39cc954eaba 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -116,6 +116,7 @@ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN = ARCH_KMALLOC_MINALIGN;
const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE;
const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE = GENLMSG_DEFAULT_SIZE;
+const unsigned long long RUST_CONST_HELPER_SZ_4G = SZ_4G;
const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC;
const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL;
const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
diff --git a/rust/kernel/sizes.rs b/rust/kernel/sizes.rs
index 188e00c2b8b46..760d26ebe6ff6 100644
--- a/rust/kernel/sizes.rs
+++ b/rust/kernel/sizes.rs
@@ -177,3 +177,15 @@ impl SizeConstants for $first {
}
define_sizes!(u32, u64, usize);
+
+/// Large size constants (≥ 4 GiB).
+///
+/// Only implemented for [`u64`].
+pub trait LargeSizeConstants {
+ /// `0x1_0000_0000`.
+ const SZ_4G: Self;
+}
+
+impl LargeSizeConstants for u64 {
+ const SZ_4G: Self = bindings::SZ_4G;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 02/10] rust: task: add task_size()
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 01/10] rust: sizes: add SZ_4G constant Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 03/10] rust: sync: arc: relax `ForeignOwnable` for `Arc<T>` Ke Sun via B4 Relay
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Sami Tolvanen <samitolvanen@google.com>
A DRM driver that lets userspace pick GPU virtual addresses needs the
address space size of the calling task, so it can offer a GPU range
that the CPU can map at the same addresses. TASK_SIZE_OF() is a macro,
so reading it from Rust needs a helper.
Add CurrentTask::task_size(). It is bound to the current task because
TASK_SIZE_OF() falls back to TASK_SIZE on architectures that do not
define it, and TASK_SIZE describes current.
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
[ Add the <linux/mm.h> include, needed where TASK_SIZE is defined in
<asm/pgtable.h> (e.g. riscv). - Alvin ]
Co-developed-by: Alvin Sun <alvin.sun@linux.dev>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/helpers/task.c | 6 ++++++
rust/kernel/task.rs | 12 ++++++++++++
2 files changed, 18 insertions(+)
diff --git a/rust/helpers/task.c b/rust/helpers/task.c
index b46b1433a67e8..b49bd40027154 100644
--- a/rust/helpers/task.c
+++ b/rust/helpers/task.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/sched/task.h>
__rust_helper void rust_helper_might_resched(void)
@@ -13,6 +14,11 @@ __rust_helper struct task_struct *rust_helper_get_current(void)
return current;
}
+__rust_helper unsigned long rust_helper_current_task_size(void)
+{
+ return TASK_SIZE_OF(current);
+}
+
__rust_helper void rust_helper_get_task_struct(struct task_struct *t)
{
get_task_struct(t);
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index a733816fd5dd4..1978f54fe5897 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -317,6 +317,18 @@ pub fn mm(&self) -> Option<&MmWithUser> {
Some(unsafe { MmWithUser::from_raw(mm) })
}
+ /// Returns the size of the userspace address space of the current task.
+ ///
+ /// This is the first address above the userspace range, so it is smaller
+ /// for a task running in compat mode than for a native one. See
+ /// `TASK_SIZE_OF()` in [`include/linux/sched.h`](srctree/include/linux/sched.h).
+ #[doc(alias = "TASK_SIZE_OF")]
+ #[inline]
+ pub fn task_size(&self) -> usize {
+ // SAFETY: FFI call without preconditions, valid in any context.
+ unsafe { bindings::current_task_size() as usize }
+ }
+
/// Access the pid namespace of the current task.
///
/// This function does not touch the refcount of the namespace or use RCU protection.
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 03/10] rust: sync: arc: relax `ForeignOwnable` for `Arc<T>`
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 01/10] rust: sizes: add SZ_4G constant Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 02/10] rust: task: add task_size() Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 04/10] drm/tyr: add per-file VM pool Ke Sun via B4 Relay
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Alvin Sun <alvin.sun@linux.dev>
Drop the `'static` bound so that refcounted values borrowing from a
driver registration scope can be foreign-owned by the XArray
abstraction.
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/sync/arc.rs | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs
index 8ae0fe6f19ec0..9b582a815f888 100644
--- a/rust/kernel/sync/arc.rs
+++ b/rust/kernel/sync/arc.rs
@@ -363,11 +363,17 @@ pub fn into_unique_or_drop(this: Self) -> Option<Pin<UniqueArc<T>>> {
// SAFETY: The pointer returned by `into_foreign` was originally allocated as an
// `KBox<ArcInner<T>>`, so that type is what determines the alignment.
-unsafe impl<T: 'static> ForeignOwnable for Arc<T> {
+unsafe impl<T> ForeignOwnable for Arc<T> {
const FOREIGN_ALIGN: usize = <KBox<ArcInner<T>> as ForeignOwnable>::FOREIGN_ALIGN;
- type Borrowed<'a> = ArcBorrow<'a, T>;
- type BorrowedMut<'a> = Self::Borrowed<'a>;
+ type Borrowed<'a>
+ = ArcBorrow<'a, T>
+ where
+ T: 'a;
+ type BorrowedMut<'a>
+ = Self::Borrowed<'a>
+ where
+ T: 'a;
fn into_foreign(self) -> *mut c_void {
ManuallyDrop::new(self).ptr.as_ptr().cast()
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 04/10] drm/tyr: add per-file VM pool
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (2 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 03/10] rust: sync: arc: relax `ForeignOwnable` for `Arc<T>` Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-14 0:24 ` Deborah Brouwer
2026-11-09 0:36 ` [PATCH v6 05/10] drm/tyr: add user and MCU VM specifications Ke Sun via B4 Relay
` (5 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Alvin Sun <alvin.sun@linux.dev>
Userspace needs multiple independent GPU address spaces per file,
addressed by ID through the VM ioctls as in panthor. Store them in an
IdPool (capped at 32 for panthor parity) plus an XArray. On failure
the caller receives the VM for teardown through the returned error,
whether the un-stored clone or the original on an ID conflict, so an
aborted creation never leaks a VM. The pool kills every VM still
stored when the file closes.
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/gpu/drm/tyr/vm.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 139 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs
index 1dd11b2b12532..6f5518652a03f 100644
--- a/drivers/gpu/drm/tyr/vm.rs
+++ b/drivers/gpu/drm/tyr/vm.rs
@@ -33,6 +33,7 @@
}, //
},
fmt,
+ id_pool::IdPool,
impl_flags,
io::PhysAddr,
iommu::pgtable::{
@@ -41,6 +42,7 @@
ARM64LPAES1, //
},
new_mutex,
+ num::casts::FromSafeCast,
prelude::*,
sizes::{
SZ_1G,
@@ -53,7 +55,11 @@
ArcBorrow,
Mutex, //
},
- uapi, //
+ uapi,
+ xarray::{
+ AllocKind,
+ XArray, //
+ }, //
};
use crate::{
@@ -947,3 +953,135 @@ fn pt_unmap(dev: &Device, pt: &IoPageTable<'_, ARM64LPAES1>, range: Range<u64>)
Ok(())
}
+
+/// Maximum number of VMs a single file may hold, matching panthor's
+/// `PANTHOR_MAX_VMS_PER_FILE`.
+const MAX_VMS_PER_FILE: usize = 32;
+
+/// Per-open-file pool of VMs.
+#[pin_data(PinnedDrop)]
+pub(crate) struct VmPool<'drm> {
+ /// Parent device, for error reporting.
+ dev: &'drm Device<Bound>,
+ #[pin]
+ ids: Mutex<IdPool>,
+ #[pin]
+ vms: XArray<Arc<Vm<'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 {
+ dev,
+ ids <- new_mutex!(ids),
+ vms <- XArray::new(AllocKind::Alloc),
+ })
+ }
+
+ /// Stores the VM and returns the allocated ID.
+ ///
+ /// On failure - ID space exhausted or a store failure - the VM is
+ /// 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();
+ let unused = ids.find_unused_id(1).ok_or(ENOSPC)?;
+ if unused.as_usize() > MAX_VMS_PER_FILE {
+ return Err(ENOSPC);
+ }
+ Ok(unused.acquire())
+ })();
+ let id = match id {
+ Ok(id) => id,
+ Err(error) => return Err(AddError { error, vm }),
+ };
+
+ let mut vms = self.vms.lock();
+ match vms.store(id, vm.clone(), GFP_KERNEL) {
+ Ok(prev_vm) => {
+ if let Some(prev_vm) = prev_vm {
+ // An existing VM at the freshly allocated slot means the
+ // IdPool and the XArray are out of sync: the stored VM is
+ // a pool entry, so the pool kills it and the caller's VM
+ // is returned for teardown.
+ drop(vms.remove(id));
+ drop(vms);
+ prev_vm.kill();
+ self.ids.lock().release_id(id);
+ dev_err!(
+ self.dev,
+ "IdPool/XArray out of sync at VM pool id {id} (stored VM conflict)"
+ );
+ return Err(AddError { error: EBUSY, vm });
+ }
+ Ok(id as u32)
+ }
+ Err(err) => {
+ // Drop the XArray spinlock before acquiring the `ids` mutex.
+ drop(vms);
+ // Release the pooled id; the VM never entered the pool, so it
+ // is returned to the caller for teardown.
+ self.ids.lock().release_id(id);
+ Err(AddError {
+ error: err.error,
+ vm: err.value,
+ })
+ }
+ }
+ }
+
+ /// 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)) {
+ Some(vm) => vm,
+ None => return Err(EINVAL),
+ };
+ // Drop the XArray spinlock before killing, since `kill()` may sleep.
+ drop(vms);
+ // Kill the VM before handing the id back to the pool, so a VM is
+ // never reusable while still alive.
+ vm.kill();
+ self.ids.lock().release_id(usize::from_safe_cast(id));
+ Ok(())
+ }
+
+ /// 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))?;
+ Some(Arc::from(borrow))
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for VmPool<'_> {
+ fn drop(self: Pin<&mut Self>) {
+ // Kill every VM left in the pool. The ID range is bounded by
+ // `MAX_VMS_PER_FILE`, so this loop is cheap and runs at file close.
+ for id in 1..=MAX_VMS_PER_FILE {
+ // Release the XArray lock guard before killing: `kill()` may sleep.
+ let vm = self.vms.lock().remove(id);
+ if let Some(vm) = vm {
+ vm.kill();
+ }
+ }
+ }
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 05/10] drm/tyr: add user and MCU VM specifications
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (3 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 04/10] drm/tyr: add per-file VM pool Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 06/10] drm/tyr: add BO creation and lookup helpers Ke Sun via B4 Relay
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Alvin Sun <alvin.sun@linux.dev>
The MCU and user VMs need different VA layouts. Give each a dedicated
constructor: new_for_fw() builds the kernel-only 4G layout, while
new_for_user() splits the address space by task_size or a user-provided
size, rejecting oversized requests rather than clamping. The resulting
user range is what VM_CREATE reports back as user_va_range.
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/gpu/drm/tyr/fw.rs | 3 +-
drivers/gpu/drm/tyr/vm.rs | 157 +++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 143 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index aea151ff1f934..196ffc96d9d99 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -51,7 +51,6 @@
KernelBoVaAlloc, //
},
gpu::GpuInfo,
-
mmu::Mmu,
regs::{
gpu_control::{
@@ -219,7 +218,7 @@ pub(crate) fn new(
mmu: ArcBorrow<'_, Mmu<'drm>>,
gpu_info: &GpuInfo,
) -> Result<Firmware<'drm>> {
- let vm = Vm::new(dev, ddev, mmu, gpu_info)?;
+ let vm = Vm::new_for_fw(dev, ddev, mmu, gpu_info)?;
vm.activate()?;
let result = (|| {
diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs
index 6f5518652a03f..44f5046037895 100644
--- a/drivers/gpu/drm/tyr/vm.rs
+++ b/drivers/gpu/drm/tyr/vm.rs
@@ -8,6 +8,7 @@
//! mapped into hardware address space (AS) slots for GPU execution.
use core::marker::PhantomData;
+use core::num::NonZeroU64;
use core::ops::Range;
use kernel::{
@@ -45,6 +46,8 @@
num::casts::FromSafeCast,
prelude::*,
sizes::{
+ LargeSizeConstants,
+ SizeConstants,
SZ_1G,
SZ_2M,
SZ_4K, //
@@ -160,6 +163,90 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
}
}
+/// User VA size request for a user VM.
+pub(crate) enum UserVaRequest {
+ /// Split based on `task_size()` and the GPU VA range.
+ Auto,
+ /// Caller-specified size; construction guarantees `> 0`.
+ Fixed(NonZeroU64),
+}
+
+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),
+ None => Self::Auto,
+ }
+ }
+}
+
+/// Final user/kernel VA layout for a VM.
+pub(crate) struct VmLayout {
+ /// Full GPU VA range covered by this VM.
+ pub(crate) full: Range<u64>,
+ /// User-accessible VA range. Empty for MCU VMs.
+ pub(crate) user: Range<u64>,
+}
+
+impl VmLayout {
+ /// Kernel VA range, reserved for future kernel object allocation.
+ #[expect(dead_code)]
+ pub(crate) fn kernel(&self) -> Range<u64> {
+ self.user.end..self.full.end
+ }
+
+ /// Compute a user/kernel split for a user VM from the full GPU VA range and
+ /// a user request.
+ pub(crate) fn compute(full: Range<u64>, req: UserVaRequest) -> Result<Self> {
+ // Minimum VA space reserved for kernel objects (heaps, ring buffers, ...).
+ const MIN_KERNEL_VA: u64 = u64::SZ_256M;
+
+ if full.end <= MIN_KERNEL_VA {
+ return Err(EINVAL);
+ }
+
+ let user_max = full.end - MIN_KERNEL_VA;
+
+ let user_end = match req {
+ UserVaRequest::Fixed(v) => {
+ let user_size = v.get();
+ if user_size > user_max {
+ return Err(EINVAL);
+ }
+ user_size
+ }
+ UserVaRequest::Auto => {
+ let task_size = current!().task_size() as u64;
+ let candidate = if task_size < full.end {
+ task_size
+ } else {
+ // If the range exceeds 4G, split it in two so CPU and
+ // GPU share the same addresses (SVM).
+ if full.end > u64::SZ_4G {
+ full.end / 2
+ } else {
+ user_max
+ }
+ };
+ candidate.min(user_max)
+ }
+ };
+
+ let delta = full.end - user_end;
+ // Pick a kernel VA range that's a power of two, to have a clear split.
+ let kernel_va_range = 1u64 << delta.ilog2();
+ let kernel_va_start = full.end - kernel_va_range;
+ let full_start = full.start;
+
+ Ok(Self {
+ full,
+ user: full_start..kernel_va_start,
+ })
+ }
+}
+
/// Arguments for a virtual memory map operation.
struct VmMapArgs<'drm> {
/// Access permissions and caching behavior for the mapping.
@@ -335,26 +422,64 @@ pub(crate) struct Vm<'drm> {
/// Non-core part of the GPUVM. Can be used for stuff that doesn't modify the
/// internal mapping tree, like GpuVm::obtain()
gpuvm: ARef<GpuVm<GpuVmData<'drm>>>,
- /// VA range for this VM.
- va_range: Range<u64>,
+ /// VA layout for this VM.
+ pub(crate) layout: VmLayout,
}
impl<'drm> Vm<'drm> {
- /// Creates a new GPU virtual address space.
+ /// Creates the MCU/firmware VM.
///
- /// The VM is initialized with a page table configured according to the GPU's
- /// address translation capabilities and registered with the GPUVM framework.
- pub(crate) fn new(
+ /// The MCU VM is entirely kernel-managed: it has no user-accessible range.
+ pub(crate) fn new_for_fw(
+ dev: &'drm Device<Bound>,
+ ddev: &TyrDrmDevice,
+ mmu: ArcBorrow<'_, Mmu<'drm>>,
+ gpu_info: &GpuInfo,
+ ) -> Result<Arc<Vm<'drm>>> {
+ // As in panthor: the CSF MCU is a Cortex-M7 and can only address 4G.
+ let layout = VmLayout {
+ full: 0..u64::SZ_4G,
+ user: 0..0u64,
+ };
+ Self::new_internal(dev, ddev, mmu, gpu_info, layout)
+ }
+
+ /// 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,
mmu: ArcBorrow<'_, Mmu<'drm>>,
gpu_info: &GpuInfo,
+ user_va: UserVaRequest,
+ ) -> Result<Arc<Vm<'drm>>> {
+ let mmu_features = MMU_FEATURES::from_raw(gpu_info.mmu_features);
+ let va_bits = mmu_features.va_bits().get();
+ let range = 0..(1u64 << va_bits);
+
+ let layout = VmLayout::compute(range.clone(), user_va).inspect_err(|_| {
+ dev_dbg!(
+ dev,
+ "Failed to split GPU VA range {:#x}..{:#x} into user and kernel regions\n",
+ range.start,
+ range.end
+ );
+ })?;
+ Self::new_internal(dev, ddev, mmu, gpu_info, layout)
+ }
+
+ /// Initializes a VM with the given layout.
+ fn new_internal(
+ dev: &'drm Device<Bound>,
+ ddev: &TyrDrmDevice,
+ mmu: ArcBorrow<'_, Mmu<'drm>>,
+ gpu_info: &GpuInfo,
+ layout: VmLayout,
) -> Result<Arc<Vm<'drm>>> {
let mmu_features = MMU_FEATURES::from_raw(gpu_info.mmu_features);
let va_bits = mmu_features.va_bits().get();
let pa_bits = mmu_features.pa_bits().get();
- let range = 0..(1u64 << va_bits);
let reserve_range = 0..0u64;
// dummy_obj is used to initialize the GPUVM tree.
@@ -366,7 +491,7 @@ pub(crate) fn new(
c"Tyr::GpuVm",
ddev,
&*dummy_obj,
- range.clone(),
+ layout.full.clone(),
reserve_range,
GpuVmData::<'drm> {
_phantom: PhantomData::<&()>,
@@ -386,7 +511,7 @@ pub(crate) fn new(
mmu: mmu.into(),
gpuvm,
gpuvm_unique <- new_mutex!(gpuvm_unique),
- va_range: range,
+ layout,
}),
GFP_KERNEL,
)?;
@@ -417,10 +542,12 @@ fn deactivate(&self) -> Result {
/// Kills the VM by deactivating it and unmapping all regions.
pub(crate) fn kill(&self) {
- // TODO: Turn the VM into a state where it can't be used.
let _ = self.deactivate();
let _ = self
- .unmap_range(self.va_range.start, self.va_range.end - self.va_range.start)
+ .unmap_range(
+ self.layout.full.start,
+ self.layout.full.end - self.layout.full.start,
+ )
.inspect_err(|e| {
dev_err!(self.dev, "Failed to unmap range during deactivate: {:?}", e);
});
@@ -557,14 +684,14 @@ pub(crate) fn unmap_range(&self, va: u64, size: u64) -> Result {
let end = va.checked_add(size).ok_or(EINVAL)?;
- if va < self.va_range.start || end > self.va_range.end {
+ if va < self.layout.full.start || end > self.layout.full.end {
dev_err!(
self.dev,
"Unmap range {:#x}..{:#x} exceeds VM range {:#x}..{:#x}",
va,
end,
- self.va_range.start,
- self.va_range.end
+ self.layout.full.start,
+ self.layout.full.end
);
return Err(EINVAL);
}
@@ -574,7 +701,7 @@ pub(crate) fn unmap_range(&self, va: u64, size: u64) -> Result {
region: va..end,
};
- let full_vm = va == self.va_range.start && end == self.va_range.end;
+ let full_vm = va == self.layout.full.start && end == self.layout.full.end;
let mut resources = VmOpResources {
preallocated_gpuvas: if full_vm {
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 06/10] drm/tyr: add BO creation and lookup helpers
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (4 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 05/10] drm/tyr: add user and MCU VM specifications Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 07/10] drm/tyr: add VM-related ioctls Ke Sun via B4 Relay
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Alvin Sun <alvin.sun@linux.dev>
Add helpers for the BO ioctls: new_object() creates a GEM object with
the size aligned up to PAGE_SIZE, and lookup_handle() resolves a handle
for a DRM file.
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
---
drivers/gpu/drm/tyr/gem.rs | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
index 3bf3787f5c3fd..0e0989a48678f 100644
--- a/drivers/gpu/drm/tyr/gem.rs
+++ b/drivers/gpu/drm/tyr/gem.rs
@@ -9,8 +9,10 @@
use kernel::{
drm::gem::{
self,
- shmem, //
+ shmem,
+ BaseObject, //
},
+ page::PAGE_SIZE,
prelude::*,
sync::{
aref::ARef,
@@ -23,6 +25,7 @@
TyrDrmDevice,
TyrDrmDriver, //
},
+ file::TyrDrmFile,
vm::{
Vm,
VmMapFlags, //
@@ -53,6 +56,32 @@ fn new(_dev: &TyrDrmDevice, _size: usize, args: BoCreateArgs) -> impl PinInit<Se
/// Type alias for Tyr GEM buffer objects.
pub(crate) type Bo = gem::shmem::Object<BoData>;
+/// Create a new GEM buffer object.
+#[expect(dead_code)]
+pub(crate) fn new_object(ddev: &TyrDrmDevice, size: usize, flags: u32) -> Result<ARef<Bo>> {
+ if size == 0 {
+ return Err(EINVAL);
+ }
+
+ let aligned_size = size.checked_next_multiple_of(PAGE_SIZE).ok_or(EINVAL)?;
+
+ Bo::new(
+ ddev,
+ aligned_size,
+ shmem::ObjectConfig {
+ map_wc: true,
+ parent_resv_obj: None,
+ },
+ BoCreateArgs { flags },
+ )
+}
+
+/// 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)
+}
+
/// 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(
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 07/10] drm/tyr: add VM-related ioctls
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (5 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 06/10] drm/tyr: add BO creation and lookup helpers Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 08/10] rust: device: expose the device DMA attribute Ke Sun via B4 Relay
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
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 | 10 +-
drivers/gpu/drm/tyr/file.rs | 295 ++++++++++++++++++++++++++++++++++++++++--
drivers/gpu/drm/tyr/gem.rs | 1 -
drivers/gpu/drm/tyr/vm.rs | 69 ++++++++--
4 files changed, 356 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 46caf7b336d93..ba03cc4c52539 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -73,6 +73,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>,
@@ -171,6 +174,7 @@ fn probe<'bound>(
pin_init!(TyrDrmRegistrationData {
pdev,
fw: firmware,
+ mmu,
clks <- new_mutex!(Clocks {
core: core_clk,
stacks: stacks_clk,
@@ -209,7 +213,7 @@ fn drop(self: Pin<&mut Self>) {}
impl drm::Driver for TyrDrmDriver {
type Data = ();
type RegistrationData<'drm> = TyrDrmRegistrationData<'drm>;
- type File<'a> = TyrDrmFileData;
+ type File<'a> = TyrDrmFileData<'a>;
type Object = Bo;
type ParentDevice<Ctx: DeviceContext> = platform::Device<Ctx>;
@@ -218,6 +222,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..49b22e7ee4179 100644
--- a/drivers/gpu/drm/tyr/file.rs
+++ b/drivers/gpu/drm/tyr/file.rs
@@ -3,37 +3,58 @@
use kernel::{
drm::{
self,
+ gem::BaseObject,
Registered, //
},
+ num::casts::FromSafeCast,
prelude::*,
+ sizes::SizeConstants,
+ transmute::FromBytes,
uaccess::UserSlice,
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 +86,260 @@ 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 = usize::from_safe_cast(vmbind.ops.count);
+ if count == 0 {
+ return file.inner_with(|pfile| {
+ pfile.vm_pool.get(vmbind.vm_id).ok_or(EINVAL)?;
+ Ok(0)
+ });
+ }
+
+ // Panthor allows larger strides; UserSliceReader::read needs an exact one.
+ if vmbind.ops.stride as usize != size_of::<VmBindOp>() {
+ dev_dbg!(
+ ddev.as_ref(),
+ "Invalid VM_BIND op stride {} (expected {})\n",
+ vmbind.ops.stride,
+ size_of::<VmBindOp>()
+ );
+ return Err(EINVAL);
+ }
+ // TODO: support variable strides with copy_struct_from_user()
+ // semantics: zero-pad shorter structs, reject non-zero trailing bytes.
+
+ let total_len = size_of::<VmBindOp>().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)?;
+ }
+
+ 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.syncs.count != 0 {
+ dev_dbg!(vm.dev(), "VM_BIND op syncs not supported\n");
+ return Err(EINVAL);
+ }
+
+ if op.size == 0 {
+ return Ok(());
+ }
+
+ 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_dbg!(vm.dev(), "VM_BIND map op on unusable VM\n");
+ 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 = 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)
+ }
+ }
+}
+
+#[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
+ }
}
+
+// TODO: use zerocopy FromBytes when these traits are derived for bindings.
+// 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 {}
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 44f5046037895..11bc8e2c9d0be 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
@@ -1099,15 +1158,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 {
@@ -1123,7 +1179,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();
@@ -1173,7 +1228,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)) {
@@ -1190,7 +1244,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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 08/10] rust: device: expose the device DMA attribute
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (6 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 07/10] drm/tyr: add VM-related ioctls Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 09/10] drm/tyr: support write-combine BO mappings Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 10/10] drm/tyr: add BO-related ioctls Ke Sun via B4 Relay
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Sami Tolvanen <samitolvanen@google.com>
A driver may need to know whether its device is DMA-coherent, but
rust/kernel exposes no accessor for the attribute today.
Add a transparent DmaAttr wrapper around device_get_dma_attr(), with
the three attribute values (unset, non-coherent, coherent) exposed as
constants, so drivers can compare against the exact attribute they
care about.
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Co-developed-by: Alvin Sun <alvin.sun@linux.dev>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/device.rs | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 2291d85b6849e..c6381d47f51e8 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -260,6 +260,33 @@ pub unsafe fn drvdata_borrow<T>(&self) -> Pin<&T> {
}
}
+/// The DMA attribute of a device, as reported by firmware. A transparent
+/// wrapper around the kernel's `enum dev_dma_attr`, covering all its states.
+#[repr(transparent)]
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct DmaAttr(bindings::dev_dma_attr);
+
+impl DmaAttr {
+ /// The device has no reported DMA attributes.
+ pub const NOT_SUPPORTED: Self = Self(bindings::dev_dma_attr_DEV_DMA_NOT_SUPPORTED);
+
+ /// The device is not DMA-coherent.
+ pub const NON_COHERENT: Self = Self(bindings::dev_dma_attr_DEV_DMA_NON_COHERENT);
+
+ /// The device is DMA-coherent.
+ pub const COHERENT: Self = Self(bindings::dev_dma_attr_DEV_DMA_COHERENT);
+}
+
+impl Device<Bound> {
+ /// Returns the DMA attribute of the device as reported by firmware.
+ #[inline]
+ pub fn dma_attr(&self) -> DmaAttr {
+ // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer
+ // to a `struct device`.
+ unsafe { DmaAttr(bindings::device_get_dma_attr(self.as_raw())) }
+ }
+}
+
impl<Ctx: DeviceContext> Device<Ctx> {
/// Obtain the raw `struct device *`.
pub(crate) fn as_raw(&self) -> *mut bindings::device {
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 09/10] drm/tyr: support write-combine BO mappings
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (7 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 08/10] rust: device: expose the device DMA attribute Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 10/10] drm/tyr: add BO-related ioctls Ke Sun via B4 Relay
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Sami Tolvanen <samitolvanen@google.com>
Tyr maps shmem-backed buffers write-combine, but on a coherent device
they are mapped write-back. Cache the coherence at probe and thread it
through to every shmem::ObjectConfig::map_wc site.
Userspace can opt out with DRM_PANTHOR_BO_WB_MMAP, which maps the
buffer cached instead.
The shmem layer zero-inits pages through a cached mapping, and those
stale lines can land over later uncached writes, so fetch the sg_table
at creation to flush them via dma_map_sgtable() before anyone accesses
them uncached. Taking the bound device keeps the unsafe
as_bound() call out of BO creation.
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Co-developed-by: Alvin Sun <alvin.sun@linux.dev>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/gpu/drm/tyr/driver.rs | 13 ++++++++-
drivers/gpu/drm/tyr/file.rs | 1 +
drivers/gpu/drm/tyr/fw.rs | 4 ++-
drivers/gpu/drm/tyr/gem.rs | 61 ++++++++++++++++++++++++++++++++++---------
drivers/gpu/drm/tyr/vm.rs | 9 ++++---
5 files changed, 71 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index ba03cc4c52539..6869ab345d8fd 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -9,7 +9,8 @@
Bound,
Core,
Device,
- DeviceContext, //
+ DeviceContext,
+ DmaAttr, //
},
dma::{
Device as DmaDevice,
@@ -87,6 +88,12 @@ pub(crate) struct TyrDrmRegistrationData<'drm> {
/// GPU information read from hardware during probe.
pub(crate) gpu_info: GpuInfo,
+
+ /// Whether the device is reported as DMA-coherent by firmware.
+ ///
+ /// Cached at probe via `device_get_dma_attr()`. Drives the BO
+ /// cacheability policy in `crate::gem::should_map_wc`.
+ pub(crate) coherent: bool,
}
fn issue_soft_reset(dev: &Device, iomem: &IoMem<'_>) -> Result {
@@ -151,6 +158,8 @@ fn probe<'bound>(
// other threads of execution.
unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(pa_bits)?)? };
+ let coherent = pdev.as_ref().dma_attr() == DmaAttr::COHERENT;
+
let unreg_dev = drm::UnregisteredDevice::<TyrDrmDriver>::new(pdev, Ok(()))?;
let mmu = Mmu::new(pdev.as_ref(), iomem.as_arc_borrow(), &gpu_info)?;
@@ -161,6 +170,7 @@ fn probe<'bound>(
&unreg_dev,
mmu.as_arc_borrow(),
&gpu_info,
+ coherent,
)?;
firmware.boot()?;
@@ -186,6 +196,7 @@ fn probe<'bound>(
}),
iomem,
gpu_info,
+ coherent,
}),
)},
_: { dev_dbg!(pdev, "Tyr initialized correctly.") },
diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs
index 49b22e7ee4179..b982e8db2c4c1 100644
--- a/drivers/gpu/drm/tyr/file.rs
+++ b/drivers/gpu/drm/tyr/file.rs
@@ -109,6 +109,7 @@ pub(crate) fn vm_create(
pfile.reg.mmu.as_arc_borrow(),
&pfile.reg.gpu_info,
UserVaRequest::from_uapi(vmcreate.user_va_range),
+ pfile.reg.coherent,
)?;
let user_va_range = vm.layout.user.end;
let id = pfile.vm_pool.add(vm).map_err(|e| {
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index 196ffc96d9d99..1c77bf109a9be 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -217,8 +217,9 @@ pub(crate) fn new(
ddev: &TyrDrmDevice,
mmu: ArcBorrow<'_, Mmu<'drm>>,
gpu_info: &GpuInfo,
+ coherent: bool,
) -> Result<Firmware<'drm>> {
- let vm = Vm::new_for_fw(dev, ddev, mmu, gpu_info)?;
+ let vm = Vm::new_for_fw(dev, ddev, mmu, gpu_info, coherent)?;
vm.activate()?;
let result = (|| {
@@ -235,6 +236,7 @@ pub(crate) fn new(
size,
KernelBoVaAlloc::Explicit(va),
parsed.vm_map_flags,
+ coherent,
)?;
let section_start = parsed.data_range.start as usize;
diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
index 60f6363995a5a..55e2d67b96d98 100644
--- a/drivers/gpu/drm/tyr/gem.rs
+++ b/drivers/gpu/drm/tyr/gem.rs
@@ -7,6 +7,10 @@
use core::ops::Range;
use kernel::{
+ device::{
+ Bound,
+ Device, //
+ },
drm::gem::{
self,
shmem,
@@ -17,7 +21,8 @@
sync::{
aref::ARef,
Arc, //
- }, //
+ },
+ uapi, //
};
use crate::{
@@ -38,6 +43,14 @@ pub(crate) struct BoData {
flags: u32,
}
+impl BoData {
+ /// Returns the flags the BO was created with.
+ #[expect(dead_code)]
+ pub(crate) fn flags(&self) -> u32 {
+ self.flags
+ }
+}
+
/// Provides a way to pass arguments when creating BoData
/// as required by the gem::DriverObject trait.
pub(crate) struct BoCreateArgs {
@@ -56,24 +69,48 @@ fn new(_dev: &TyrDrmDevice, _size: usize, args: BoCreateArgs) -> impl PinInit<Se
/// Type alias for Tyr GEM buffer objects.
pub(crate) type Bo = gem::shmem::Object<BoData>;
+/// Returns whether a BO should be mapped write-combine.
+pub(crate) fn should_map_wc(coherent: bool, flags: u32) -> bool {
+ if coherent {
+ return false;
+ }
+
+ if flags & uapi::drm_panthor_bo_flags_DRM_PANTHOR_BO_WB_MMAP != 0 {
+ return false;
+ }
+
+ true
+}
+
/// Create a new GEM buffer object.
#[expect(dead_code)]
-pub(crate) fn new_object(ddev: &TyrDrmDevice, size: usize, flags: u32) -> Result<ARef<Bo>> {
+pub(crate) fn new_object(
+ ddev: &TyrDrmDevice,
+ pdev: &Device<Bound>,
+ size: usize,
+ flags: u32,
+ coherent: bool,
+) -> Result<ARef<Bo>> {
if size == 0 {
return Err(EINVAL);
}
let aligned_size = size.checked_next_multiple_of(PAGE_SIZE).ok_or(EINVAL)?;
- Bo::new(
+ let map_wc = should_map_wc(coherent, flags);
+ let bo = Bo::new(
ddev,
aligned_size,
shmem::ObjectConfig {
- map_wc: true,
+ map_wc,
parent_resv_obj: None,
},
BoCreateArgs { flags },
- )
+ )?;
+
+ bo.sg_table(pdev)?;
+
+ Ok(bo)
}
/// Look up a GEM object by handle for a DRM file.
@@ -82,18 +119,17 @@ pub(crate) fn lookup_handle(file: &TyrDrmFile, handle: u32) -> Result<ARef<Bo>>
}
/// 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(
+pub(crate) fn new_dummy_object(ddev: &TyrDrmDevice, coherent: bool) -> Result<ARef<Bo>> {
+ // FIXME: use a Rust resv-object abstraction once available, rather than a real BO.
+ Bo::new(
ddev,
4096,
shmem::ObjectConfig {
- map_wc: true,
+ map_wc: should_map_wc(coherent, 0),
parent_resv_obj: None,
},
BoCreateArgs { flags: 0 },
- )?;
-
- Ok(bo)
+ )
}
/// Specifies how to choose a GPU virtual address for a [`KernelBo`].
@@ -131,6 +167,7 @@ pub(crate) fn new(
size: u64,
va_alloc: KernelBoVaAlloc,
flags: VmMapFlags,
+ coherent: bool,
) -> Result<Self> {
if size == 0 {
dev_err!(vm.dev(), "Cannot create KernelBo with size 0");
@@ -146,7 +183,7 @@ pub(crate) fn new(
ddev,
bo_size,
shmem::ObjectConfig {
- map_wc: true,
+ map_wc: should_map_wc(coherent, 0),
parent_resv_obj: None,
},
BoCreateArgs { flags: 0 },
diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs
index 11bc8e2c9d0be..6d312c3b2ccd1 100644
--- a/drivers/gpu/drm/tyr/vm.rs
+++ b/drivers/gpu/drm/tyr/vm.rs
@@ -473,13 +473,14 @@ pub(crate) fn new_for_fw(
ddev: &TyrDrmDevice,
mmu: ArcBorrow<'_, Mmu<'drm>>,
gpu_info: &GpuInfo,
+ coherent: bool,
) -> Result<Arc<Vm<'drm>>> {
// As in panthor: the CSF MCU is a Cortex-M7 and can only address 4G.
let layout = VmLayout {
full: 0..u64::SZ_4G,
user: 0..0u64,
};
- Self::new_internal(dev, ddev, mmu, gpu_info, layout)
+ Self::new_internal(dev, ddev, mmu, gpu_info, layout, coherent)
}
/// Creates a user VM, splitting the GPU VA range per `user_va`.
@@ -489,6 +490,7 @@ pub(crate) fn new_for_user(
mmu: ArcBorrow<'_, Mmu<'drm>>,
gpu_info: &GpuInfo,
user_va: UserVaRequest,
+ coherent: bool,
) -> Result<Arc<Vm<'drm>>> {
let mmu_features = MMU_FEATURES::from_raw(gpu_info.mmu_features);
let va_bits = mmu_features.va_bits().get();
@@ -502,7 +504,7 @@ pub(crate) fn new_for_user(
range.end
);
})?;
- Self::new_internal(dev, ddev, mmu, gpu_info, layout)
+ Self::new_internal(dev, ddev, mmu, gpu_info, layout, coherent)
}
/// Initializes a VM with the given layout.
@@ -512,6 +514,7 @@ fn new_internal(
mmu: ArcBorrow<'_, Mmu<'drm>>,
gpu_info: &GpuInfo,
layout: VmLayout,
+ coherent: bool,
) -> Result<Arc<Vm<'drm>>> {
let mmu_features = MMU_FEATURES::from_raw(gpu_info.mmu_features);
let va_bits = mmu_features.va_bits().get();
@@ -520,7 +523,7 @@ fn new_internal(
let reserve_range = 0..0u64;
// dummy_obj is used to initialize the GPUVM tree.
- let dummy_obj = gem::new_dummy_object(ddev).inspect_err(|e| {
+ let dummy_obj = gem::new_dummy_object(ddev, coherent).inspect_err(|e| {
dev_err!(dev, "Failed to create dummy GEM object: {:?}", e);
})?;
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 10/10] drm/tyr: add BO-related ioctls
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
` (8 preceding siblings ...)
2026-11-09 0:36 ` [PATCH v6 09/10] drm/tyr: support write-combine BO mappings Ke Sun via B4 Relay
@ 2026-11-09 0:36 ` Ke Sun via B4 Relay
9 siblings, 0 replies; 12+ messages in thread
From: Ke Sun via B4 Relay @ 2026-11-09 0:36 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold
Cc: Sami Tolvanen, rust-for-linux, dri-devel, driver-core, Ke Sun,
Alvin Sun
From: Alvin Sun <alvin.sun@linux.dev>
Implement BO_CREATE and BO_MMAP_OFFSET so userspace can allocate GPU
buffers and obtain a DRM mmap offset for the generic mmap path.
BO_CREATE page-aligns the requested size and accepts NO_MMAP or WB_MMAP,
which are mutually exclusive; an exclusive VM returns EOPNOTSUPP.
BO_MMAP_OFFSET rejects NO_MMAP objects, which must never be CPU-mapped.
Co-developed-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/gpu/drm/tyr/driver.rs | 2 +
drivers/gpu/drm/tyr/file.rs | 94 +++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/tyr/gem.rs | 2 -
3 files changed, 96 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 6869ab345d8fd..8632e6e820c97 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -237,6 +237,8 @@ impl drm::Driver for TyrDrmDriver {
(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),
+ (PANTHOR_BO_CREATE, drm_panthor_bo_create, ioctl::RENDER_ALLOW, TyrDrmFileData::bo_create),
+ (PANTHOR_BO_MMAP_OFFSET, drm_panthor_bo_mmap_offset, ioctl::RENDER_ALLOW, TyrDrmFileData::bo_mmap_offset),
}
}
diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs
index b982e8db2c4c1..f99bb2992d87a 100644
--- a/drivers/gpu/drm/tyr/file.rs
+++ b/drivers/gpu/drm/tyr/file.rs
@@ -242,6 +242,100 @@ pub(crate) fn vm_get_state(
Ok(0)
})
}
+
+ pub(crate) fn bo_create(
+ ddev: &TyrDrmDevice<Registered>,
+ reg_data: &TyrDrmRegistrationData<'_>,
+ bocreate: &mut uapi::drm_panthor_bo_create,
+ file: &TyrDrmFile,
+ ) -> Result<u32> {
+ if bocreate.size == 0 || bocreate.pad != 0 {
+ dev_dbg!(
+ ddev.as_ref(),
+ "Invalid BO_CREATE params: size={}, pad={}\n",
+ bocreate.size,
+ bocreate.pad
+ );
+ return Err(EINVAL);
+ }
+
+ let no_mmap = uapi::drm_panthor_bo_flags_DRM_PANTHOR_BO_NO_MMAP;
+ let wb_mmap = uapi::drm_panthor_bo_flags_DRM_PANTHOR_BO_WB_MMAP;
+ let valid_flags = no_mmap | wb_mmap;
+
+ if bocreate.flags & !valid_flags != 0 {
+ dev_dbg!(
+ ddev.as_ref(),
+ "Invalid BO_CREATE flags: {:#x}\n",
+ bocreate.flags
+ );
+ return Err(EINVAL);
+ }
+ if bocreate.flags & no_mmap != 0 && bocreate.flags & wb_mmap != 0 {
+ dev_dbg!(ddev.as_ref(), "BO_CREATE: NO_MMAP conflicts with WB_MMAP\n");
+ return Err(EINVAL);
+ }
+
+ if bocreate.exclusive_vm_id != 0 {
+ dev_dbg!(ddev.as_ref(), "BO_CREATE: exclusive_vm_id not supported\n");
+ return Err(EOPNOTSUPP);
+ }
+
+ let size = usize::try_from(bocreate.size).map_err(|_| {
+ dev_dbg!(
+ ddev.as_ref(),
+ "BO_CREATE size {:#x} too large\n",
+ bocreate.size
+ );
+ EINVAL
+ })?;
+ let bo = file.inner_with(|pfile| {
+ crate::gem::new_object(
+ ddev,
+ pfile.reg.pdev.as_ref(),
+ size,
+ bocreate.flags,
+ reg_data.coherent,
+ )
+ })?;
+ bocreate.handle = bo.create_handle(file)?;
+ bocreate.size = u64::from_safe_cast(bo.size());
+
+ Ok(0)
+ }
+
+ pub(crate) fn bo_mmap_offset(
+ ddev: &TyrDrmDevice<Registered>,
+ _reg_data: &TyrDrmRegistrationData<'_>,
+ bommap: &mut uapi::drm_panthor_bo_mmap_offset,
+ file: &TyrDrmFile,
+ ) -> Result<u32> {
+ if bommap.pad != 0 {
+ dev_dbg!(
+ ddev.as_ref(),
+ "BO mmap offset pad not zero: {}\n",
+ bommap.pad
+ );
+ return Err(EINVAL);
+ }
+
+ let bo = crate::gem::lookup_handle(file, bommap.handle).inspect_err(|_| {
+ dev_dbg!(ddev.as_ref(), "Invalid BO mmap handle: {}\n", bommap.handle);
+ })?;
+ if bo.flags() & uapi::drm_panthor_bo_flags_DRM_PANTHOR_BO_NO_MMAP != 0 {
+ dev_dbg!(ddev.as_ref(), "BO mmap offset on NO_MMAP object\n");
+ return Err(EPERM);
+ }
+ bommap.offset = bo.create_mmap_offset().inspect_err(|_| {
+ dev_err!(
+ ddev.as_ref(),
+ "Failed to create mmap offset for handle {}\n",
+ bommap.handle
+ );
+ })?;
+
+ Ok(0)
+ }
}
fn vm_bind_exec_op(vm: &Vm<'_>, file: &TyrDrmFile, op: &VmBindOp) -> Result {
diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
index 55e2d67b96d98..d2ef121c8b1b6 100644
--- a/drivers/gpu/drm/tyr/gem.rs
+++ b/drivers/gpu/drm/tyr/gem.rs
@@ -45,7 +45,6 @@ pub(crate) struct BoData {
impl BoData {
/// Returns the flags the BO was created with.
- #[expect(dead_code)]
pub(crate) fn flags(&self) -> u32 {
self.flags
}
@@ -83,7 +82,6 @@ pub(crate) fn should_map_wc(coherent: bool, flags: u32) -> bool {
}
/// Create a new GEM buffer object.
-#[expect(dead_code)]
pub(crate) fn new_object(
ddev: &TyrDrmDevice,
pdev: &Device<Bound>,
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v6 04/10] drm/tyr: add per-file VM pool
2026-11-09 0:36 ` [PATCH v6 04/10] drm/tyr: add per-file VM pool Ke Sun via B4 Relay
@ 2026-11-14 0:24 ` Deborah Brouwer
0 siblings, 0 replies; 12+ messages in thread
From: Deborah Brouwer @ 2026-11-14 0:24 UTC (permalink / raw)
To: Ke Sun
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul, David Airlie,
Simona Vetter, Greg Kroah-Hartman, Rafael J. Wysocki,
Johan Hovold, Sami Tolvanen, rust-for-linux, dri-devel,
driver-core, Alvin Sun
On Mon, Nov 09, 2026 at 08:36:47AM +0800, Ke Sun wrote:
> From: Alvin Sun <alvin.sun@linux.dev>
>
> Userspace needs multiple independent GPU address spaces per file,
> addressed by ID through the VM ioctls as in panthor. Store them in an
> IdPool (capped at 32 for panthor parity) plus an XArray. On failure
> the caller receives the VM for teardown through the returned error,
> whether the un-stored clone or the original on an ID conflict, so an
> aborted creation never leaks a VM. The pool kills every VM still
> stored when the file closes.
>
> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
> ---
> drivers/gpu/drm/tyr/vm.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 139 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs
> index 1dd11b2b12532..6f5518652a03f 100644
> --- a/drivers/gpu/drm/tyr/vm.rs
> +++ b/drivers/gpu/drm/tyr/vm.rs
> @@ -33,6 +33,7 @@
> }, //
> },
> fmt,
> + id_pool::IdPool,
> impl_flags,
> io::PhysAddr,
> iommu::pgtable::{
> @@ -41,6 +42,7 @@
> ARM64LPAES1, //
> },
> new_mutex,
> + num::casts::FromSafeCast,
> prelude::*,
> sizes::{
> SZ_1G,
> @@ -53,7 +55,11 @@
> ArcBorrow,
> Mutex, //
> },
> - uapi, //
> + uapi,
> + xarray::{
> + AllocKind,
> + XArray, //
> + }, //
> };
>
> use crate::{
> @@ -947,3 +953,135 @@ fn pt_unmap(dev: &Device, pt: &IoPageTable<'_, ARM64LPAES1>, range: Range<u64>)
>
> Ok(())
> }
> +
> +/// Maximum number of VMs a single file may hold, matching panthor's
> +/// `PANTHOR_MAX_VMS_PER_FILE`.
> +const MAX_VMS_PER_FILE: usize = 32;
> +
> +/// Per-open-file pool of VMs.
> +#[pin_data(PinnedDrop)]
> +pub(crate) struct VmPool<'drm> {
> + /// Parent device, for error reporting.
> + dev: &'drm Device<Bound>,
> + #[pin]
> + ids: Mutex<IdPool>,
> + #[pin]
> + vms: XArray<Arc<Vm<'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();
I sent a reply to sashiko-review lists but i meant it to go to the whole
thread. Basically, just wanted to suggest using IdPool::with_capacity()
to make sure we can create 32 vms on 32 bit builds.
https://lore.kernel.org/dri-devel/avZlPvHqlxmu2hNO@um790/
> + pin_init!(Self {
> + dev,
> + ids <- new_mutex!(ids),
> + vms <- XArray::new(AllocKind::Alloc),
> + })
> + }
> +
> + /// Stores the VM and returns the allocated ID.
> + ///
> + /// On failure - ID space exhausted or a store failure - the VM is
> + /// 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();
> + let unused = ids.find_unused_id(1).ok_or(ENOSPC)?;
> + if unused.as_usize() > MAX_VMS_PER_FILE {
> + return Err(ENOSPC);
Turns out since panthor uses xa_alloc() it will return
" * -EBUSY if there are no free entries in @limit." so we better do
the same here.
> + }
> + Ok(unused.acquire())
> + })();
> + let id = match id {
> + Ok(id) => id,
> + Err(error) => return Err(AddError { error, vm }),
> + };
> +
> + let mut vms = self.vms.lock();
> + match vms.store(id, vm.clone(), GFP_KERNEL) {
> + Ok(prev_vm) => {
> + if let Some(prev_vm) = prev_vm {
> + // An existing VM at the freshly allocated slot means the
> + // IdPool and the XArray are out of sync: the stored VM is
> + // a pool entry, so the pool kills it and the caller's VM
> + // is returned for teardown.
> + drop(vms.remove(id));
> + drop(vms);
> + prev_vm.kill();
> + self.ids.lock().release_id(id);
> + dev_err!(
> + self.dev,
> + "IdPool/XArray out of sync at VM pool id {id} (stored VM conflict)"
> + );
> + return Err(AddError { error: EBUSY, vm });
> + }
> + Ok(id as u32)
> + }
> + Err(err) => {
> + // Drop the XArray spinlock before acquiring the `ids` mutex.
> + drop(vms);
> + // Release the pooled id; the VM never entered the pool, so it
> + // is returned to the caller for teardown.
> + self.ids.lock().release_id(id);
> + Err(AddError {
> + error: err.error,
> + vm: err.value,
> + })
> + }
> + }
> + }
> +
> + /// 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)) {
> + Some(vm) => vm,
> + None => return Err(EINVAL),
> + };
> + // Drop the XArray spinlock before killing, since `kill()` may sleep.
> + drop(vms);
> + // Kill the VM before handing the id back to the pool, so a VM is
> + // never reusable while still alive.
> + vm.kill();
> + self.ids.lock().release_id(usize::from_safe_cast(id));
> + Ok(())
> + }
> +
> + /// 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))?;
> + Some(Arc::from(borrow))
> + }
> +}
> +
> +#[pinned_drop]
> +impl PinnedDrop for VmPool<'_> {
> + fn drop(self: Pin<&mut Self>) {
> + // Kill every VM left in the pool. The ID range is bounded by
> + // `MAX_VMS_PER_FILE`, so this loop is cheap and runs at file close.
> + for id in 1..=MAX_VMS_PER_FILE {
> + // Release the XArray lock guard before killing: `kill()` may sleep.
> + let vm = self.vms.lock().remove(id);
> + if let Some(vm) = vm {
> + vm.kill();
> + }
> + }
> + }
> +}
>
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-11-14 0:25 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-09 0:36 [PATCH v6 00/10] drm/tyr: add VM and BO ioctl support Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 01/10] rust: sizes: add SZ_4G constant Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 02/10] rust: task: add task_size() Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 03/10] rust: sync: arc: relax `ForeignOwnable` for `Arc<T>` Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 04/10] drm/tyr: add per-file VM pool Ke Sun via B4 Relay
2026-11-14 0:24 ` Deborah Brouwer
2026-11-09 0:36 ` [PATCH v6 05/10] drm/tyr: add user and MCU VM specifications Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 06/10] drm/tyr: add BO creation and lookup helpers Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 07/10] drm/tyr: add VM-related ioctls Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 08/10] rust: device: expose the device DMA attribute Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 09/10] drm/tyr: support write-combine BO mappings Ke Sun via B4 Relay
2026-11-09 0:36 ` [PATCH v6 10/10] drm/tyr: add BO-related ioctls Ke Sun via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox