[PATCH v2 2/3] drm/panthor: Report the CS fatal errors through GROUP_GET_STATE
From: Christian Gmeiner
Date: Wed Nov 04 2026 - 03:01:31 EST
From: Christian Gmeiner <cgmeiner@xxxxxxxxxx>
Keep the raw CS_FATAL and CS_FATAL_INFO in the queue that hit them and
return them through GROUP_GET_STATE in an array indexed by queue.
Userspace decodes the values. No memory is allocated in the fatal path.
A queue stops at its first fatal error, so one slot per queue is
enough. The slot is a struct drm_panthor_queue_fatal, which is the uAPI
struct, so the ioctl can copy it with one assignment. The CS_FATAL
event handler writes the slot under events_lock. The ioctl reads it
under the same lock. The slot is never cleared, so the values can be
read again after the call.
The ioctl copies the slots of all queues to a stack array while the
locks are held and copies them to userspace after the locks are
dropped. It fills at most one entry per queue of the group and leaves
the rest of the user array untouched.
A queue can be flagged in fatal_queues without a CS_FATAL, for example
when the group was killed by a VM fault. Its entry then has a zero
exception. The VM fault itself is available through VM_GET_STATE.
Old userspace that passes the shorter struct still works, the DRM core
zero-fills the missing tail. A zero count skips the array.
Link: https://lore.kernel.org/r/20251215115457.2137485-3-lukas.zapolskas@xxxxxxx
Link: https://lore.kernel.org/r/20251215115457.2137485-6-lukas.zapolskas@xxxxxxx
Signed-off-by: Christian Gmeiner <cgmeiner@xxxxxxxxxx>
---
drivers/gpu/drm/panthor/panthor_drv.c | 20 ++++++++++++++++++--
drivers/gpu/drm/panthor/panthor_sched.c | 32 +++++++++++++++++++++++++++++++-
drivers/gpu/drm/panthor/panthor_sched.h | 5 ++++-
include/uapi/drm/panthor_drm.h | 30 ++++++++++++++++++++++++++++++
4 files changed, 83 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 7572cd7d6423..fb5fdf848dec 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -181,7 +181,8 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride,
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_submit, syncs), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_create, ringbuf_size), \
PANTHOR_UOBJ_DECL(struct drm_panthor_vm_bind_op, syncs), \
- PANTHOR_UOBJ_DECL(struct drm_panthor_bo_sync_op, size))
+ PANTHOR_UOBJ_DECL(struct drm_panthor_bo_sync_op, size), \
+ PANTHOR_UOBJ_DECL(struct drm_panthor_queue_fatal, exception_info))
/**
* PANTHOR_UOBJ_SET() - Copy a kernel object to a user object.
@@ -1254,10 +1255,25 @@ static int panthor_ioctl_group_create(struct drm_device *ddev, void *data,
static int panthor_ioctl_group_get_state(struct drm_device *ddev, void *data,
struct drm_file *file)
{
+ struct drm_panthor_queue_fatal fatals[MAX_CS_PER_CSG];
struct panthor_file *pfile = file->driver_priv;
struct drm_panthor_group_get_state *args = data;
+ u64 usr_ptr = args->queue_fatals.array;
+ u32 stride = args->queue_fatals.stride;
+ u32 count;
+ int ret;
- return panthor_group_get_state(pfile, args);
+ ret = panthor_group_get_state(pfile, args, fatals, &count);
+ if (ret)
+ return ret;
+
+ for (u32 i = 0; i < count; i++) {
+ ret = PANTHOR_UOBJ_SET(usr_ptr + (u64)i * stride, stride, fatals[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static int panthor_ioctl_tiler_heap_create(struct drm_device *ddev, void *data,
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index a343c5180e01..40400b2e4551 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -485,6 +485,15 @@ struct panthor_queue {
/** @profiling.seqno: Index of the next available profiling information slot. */
u32 seqno;
} profiling;
+
+ /**
+ * @fatal: Last CS_FATAL reported on this queue.
+ *
+ * Never cleared, a later CS_FATAL overwrites it. A zero exception
+ * means no CS_FATAL was reported. Protected by
+ * panthor_scheduler::events_lock.
+ */
+ struct drm_panthor_queue_fatal fatal;
};
/**
@@ -1412,10 +1421,20 @@ cs_slot_process_fatal_event_locked(struct panthor_device *ptdev,
info = cs_iface->output->fatal_info;
if (group) {
+ struct panthor_queue *queue = cs_id < group->queue_count ?
+ group->queues[cs_id] : NULL;
+
drm_warn(&ptdev->base, "CS_FATAL: pid=%d, comm=%s\n",
group->task_info.pid, group->task_info.comm);
atomic_or(BIT(cs_id), &group->fatal_queues);
+
+ if (queue) {
+ queue->fatal = (struct drm_panthor_queue_fatal){
+ .exception = fatal,
+ .exception_info = info,
+ };
+ }
}
if (CS_EXCEPTION_TYPE(fatal) == DRM_PANTHOR_EXCEPTION_CS_UNRECOVERABLE) {
@@ -3818,8 +3837,11 @@ static struct panthor_group *group_from_handle(struct panthor_group_pool *pool,
}
int panthor_group_get_state(struct panthor_file *pfile,
- struct drm_panthor_group_get_state *get_state)
+ struct drm_panthor_group_get_state *get_state,
+ struct drm_panthor_queue_fatal *fatals,
+ u32 *fatal_count)
{
+ struct drm_panthor_obj_array queue_fatals = get_state->queue_fatals;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_device *ptdev = pfile->ptdev;
struct panthor_scheduler *sched = ptdev->scheduler;
@@ -3833,6 +3855,9 @@ int panthor_group_get_state(struct panthor_file *pfile,
return -EINVAL;
memset(get_state, 0, sizeof(*get_state));
+ get_state->queue_fatals = queue_fatals;
+
+ *fatal_count = min(queue_fatals.count, group->queue_count);
mutex_lock(&sched->lock);
if (atomic_read(&group->timedout))
@@ -3844,6 +3869,11 @@ int panthor_group_get_state(struct panthor_file *pfile,
if (group->innocent)
get_state->state |= DRM_PANTHOR_GROUP_STATE_INNOCENT;
+
+ scoped_guard(spinlock, &sched->events_lock) {
+ for (u32 i = 0; i < *fatal_count; i++)
+ fatals[i] = group->queues[i]->fatal;
+ }
mutex_unlock(&sched->lock);
group_put(group);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
index d87cbdba4d40..dd3d31c06cf1 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.h
+++ b/drivers/gpu/drm/panthor/panthor_sched.h
@@ -14,6 +14,7 @@ struct drm_minor;
struct drm_panthor_group_create;
struct drm_panthor_queue_create;
struct drm_panthor_group_get_state;
+struct drm_panthor_queue_fatal;
struct drm_panthor_queue_submit;
struct panthor_device;
struct panthor_file;
@@ -26,7 +27,9 @@ int panthor_group_create(struct panthor_file *pfile,
u64 drm_client_id);
int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle);
int panthor_group_get_state(struct panthor_file *pfile,
- struct drm_panthor_group_get_state *get_state);
+ struct drm_panthor_group_get_state *get_state,
+ struct drm_panthor_queue_fatal *fatals,
+ u32 *fatal_count);
struct drm_sched_job *
panthor_job_create(struct panthor_file *pfile,
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index 85e197d7a528..c9a681762b96 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -1072,6 +1072,27 @@ enum drm_panthor_group_state_flags {
DRM_PANTHOR_GROUP_STATE_INNOCENT = 1 << 2,
};
+/**
+ * struct drm_panthor_queue_fatal - Fatal error of a single queue.
+ */
+struct drm_panthor_queue_fatal {
+ /**
+ * @exception: Raw CS_FATAL value.
+ *
+ * Zero if no CS_FATAL was reported on this queue. A queue can be
+ * flagged in drm_panthor_group_get_state::fatal_queues without a
+ * CS_FATAL, for instance when the group was killed by a VM fault.
+ * A later CS_FATAL overwrites the previous one.
+ */
+ __u32 exception;
+
+ /** @pad: Will be zero on return. */
+ __u32 pad;
+
+ /** @exception_info: Raw CS_FATAL_INFO value, valid if @exception is non-zero. */
+ __u64 exception_info;
+};
+
/**
* struct drm_panthor_group_get_state - Arguments passed to DRM_IOCTL_PANTHOR_GROUP_GET_STATE
*
@@ -1093,6 +1114,15 @@ struct drm_panthor_group_get_state {
/** @pad: MBZ */
__u32 pad;
+
+ /**
+ * @queue_fatals: Array of struct drm_panthor_queue_fatal, indexed by queue.
+ *
+ * Entry N describes the queue at index N of the group creation array.
+ * The driver fills at most as many entries as the group has queues and
+ * leaves the rest untouched. A zero count skips the array.
+ */
+ struct drm_panthor_obj_array queue_fatals;
};
/**
--
2.56.0