[PATCH v2 1/3] drm/panthor: Report the last VM fault through VM_GET_STATE

From: Christian Gmeiner

Date: Wed Nov 04 2026 - 02:53:58 EST


From: Christian Gmeiner <cgmeiner@xxxxxxxxxx>

Keep the raw AS_FAULTSTATUS and AS_FAULTADDRESS of the last fault in
the VM and return them through VM_GET_STATE. Userspace decodes the
values. No memory is allocated in the fault path.

The MMU irq handler records the fault under the AS slots lock. The
ioctl reads it under the same lock. The VM stores the fault as a
struct drm_panthor_vm_fault, which is the uAPI struct, so the ioctl
can copy it with one assignment.

The unhandled_fault flag is cleared when the VM is re-activated. The
stored fault is not. So userspace can still query it after the group
that hit the fault was killed.

Old userspace that passes the shorter struct still works, the DRM core
zero-fills the missing tail.

Link: https://lore.kernel.org/r/20251215115457.2137485-4-lukas.zapolskas@xxxxxxx
Signed-off-by: Christian Gmeiner <cgmeiner@xxxxxxxxxx>
---
drivers/gpu/drm/panthor/panthor_drv.c | 2 ++
drivers/gpu/drm/panthor/panthor_mmu.c | 33 +++++++++++++++++++++++++++++++--
drivers/gpu/drm/panthor/panthor_mmu.h | 2 ++
include/uapi/drm/panthor_drm.h | 22 ++++++++++++++++++++++
4 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index dc8d0191aef4..7572cd7d6423 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1471,6 +1471,8 @@ static int panthor_ioctl_vm_get_state(struct drm_device *ddev, void *data,
else
args->state = DRM_PANTHOR_VM_STATE_USABLE;

+ panthor_vm_get_fault(vm, &args->fault);
+
panthor_vm_put(vm);
return 0;
}
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index d1c29eec46f2..6fc5f3d381c1 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -389,6 +389,15 @@ struct panthor_vm {
*/
bool unhandled_fault;

+ /**
+ * @fault: Last fault on this VM.
+ *
+ * A new fault overwrites the previous one. Unlike @unhandled_fault,
+ * this is not cleared when the VM is re-activated. Protected by
+ * panthor_mmu::as::slots_lock.
+ */
+ struct drm_panthor_vm_fault fault;
+
/** @locked_region: Information about the currently locked region currently. */
struct {
/** @locked_region.start: Start of the locked region. */
@@ -662,6 +671,19 @@ bool panthor_vm_has_unhandled_faults(struct panthor_vm *vm)
return vm->unhandled_fault;
}

+/**
+ * panthor_vm_get_fault() - Get the last fault of a VM
+ * @vm: VM to query.
+ * @fault: Returns the last fault, with a zero status if no fault happened.
+ */
+void panthor_vm_get_fault(struct panthor_vm *vm, struct drm_panthor_vm_fault *fault)
+{
+ struct panthor_device *ptdev = vm->ptdev;
+
+ guard(mutex)(&ptdev->mmu->as.slots_lock);
+ *fault = vm->fault;
+}
+
/**
* panthor_vm_is_unusable() - Check if the VM is still usable
* @vm: VM to check.
@@ -1855,6 +1877,7 @@ static void panthor_mmu_irq_handler(struct panthor_irq *pirq, u32 status)
while (status) {
u32 as = ffs(status | (status >> 16)) - 1;
u32 mask = MMU_INT_AS_FAULT(as);
+ struct panthor_vm *vm;
u64 addr;
u32 fault_status;
u32 exception_type;
@@ -1895,8 +1918,14 @@ static void panthor_mmu_irq_handler(struct panthor_irq *pirq, u32 status)
*/
gpu_write(mmu->irq.iomem, INT_CLEAR, mask);

- if (ptdev->mmu->as.slots[as].vm)
- ptdev->mmu->as.slots[as].vm->unhandled_fault = true;
+ vm = mmu->as.slots[as].vm;
+ if (vm) {
+ vm->unhandled_fault = true;
+ vm->fault = (struct drm_panthor_vm_fault){
+ .status = fault_status,
+ .address = addr,
+ };
+ }

/* Disable the MMU to kill jobs on this AS. */
panthor_mmu_as_disable(ptdev, as);
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h
index abc36e7204be..74f9f5d98c94 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.h
+++ b/drivers/gpu/drm/panthor/panthor_mmu.h
@@ -11,6 +11,7 @@
struct drm_exec;
struct drm_sched_job;
struct drm_memory_stats;
+struct drm_panthor_vm_fault;
struct panthor_gem_object;
struct panthor_heap_pool;
struct panthor_vm;
@@ -74,6 +75,7 @@ struct panthor_vm *panthor_vm_pool_get_vm(struct panthor_vm_pool *pool, u32 hand

bool panthor_vm_has_unhandled_faults(struct panthor_vm *vm);
bool panthor_vm_is_unusable(struct panthor_vm *vm);
+void panthor_vm_get_fault(struct panthor_vm *vm, struct drm_panthor_vm_fault *fault);

/*
* PANTHOR_VM_KERNEL_AUTO_VA: Use this magic address when you want the GEM
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index a2ff0f4ec691..85e197d7a528 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -743,6 +743,20 @@ enum drm_panthor_vm_state {
DRM_PANTHOR_VM_STATE_UNUSABLE,
};

+/**
+ * struct drm_panthor_vm_fault - Fault information of a VM.
+ */
+struct drm_panthor_vm_fault {
+ /** @status: Raw AS_FAULTSTATUS value, zero if no fault happened. */
+ __u32 status;
+
+ /** @pad: Will be zero on return. */
+ __u32 pad;
+
+ /** @address: Raw AS_FAULTADDRESS value, valid if @status is non-zero. */
+ __u64 address;
+};
+
/**
* struct drm_panthor_vm_get_state - Get VM state.
*/
@@ -756,6 +770,14 @@ struct drm_panthor_vm_get_state {
* Must be one of the enum drm_panthor_vm_state values.
*/
__u32 state;
+
+ /**
+ * @fault: Last fault on this VM.
+ *
+ * A new fault overwrites the previous one. It is not cleared when the
+ * VM is re-activated or after a GPU reset.
+ */
+ struct drm_panthor_vm_fault fault;
};

/**

--
2.56.0