diff mbox series

[v2,RESEND,2/5] drm: panthor: add devcoredump support

Message ID 20240821143826.3720-3-daniel.almeida@collabora.com (mailing list archive)
State New, archived
Headers show
Series Panthor devcoredump support | expand

Commit Message

Daniel Almeida Aug. 21, 2024, 2:37 p.m. UTC
Dump the GPU state using devcoredump. This is useful for debugging
purposes.

Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 drivers/gpu/drm/panthor/Kconfig         |   1 +
 drivers/gpu/drm/panthor/Makefile        |   1 +
 drivers/gpu/drm/panthor/panthor_dump.c  | 376 ++++++++++++++++++++++++
 drivers/gpu/drm/panthor/panthor_dump.h  |  21 ++
 drivers/gpu/drm/panthor/panthor_mmu.c   |  22 ++
 drivers/gpu/drm/panthor/panthor_mmu.h   |   6 +
 drivers/gpu/drm/panthor/panthor_sched.c |  51 +++-
 drivers/gpu/drm/panthor/panthor_sched.h |  10 +
 include/uapi/drm/panthor_drm.h          | 124 ++++++++
 9 files changed, 611 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/panthor/panthor_dump.c
 create mode 100644 drivers/gpu/drm/panthor/panthor_dump.h

Comments

Adrián Larumbe Aug. 22, 2024, 12:27 p.m. UTC | #1
> On 21.08.2024 11:37, Daniel Almeida wrote:
> Dump the GPU state using devcoredump. This is useful for debugging
> purposes.
> 
> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>

Reviewed-by: Adrian Larumbe <adrian.larumbe@collabora.com>

> ---
>  drivers/gpu/drm/panthor/Kconfig         |   1 +
>  drivers/gpu/drm/panthor/Makefile        |   1 +
>  drivers/gpu/drm/panthor/panthor_dump.c  | 376 ++++++++++++++++++++++++
>  drivers/gpu/drm/panthor/panthor_dump.h  |  21 ++
>  drivers/gpu/drm/panthor/panthor_mmu.c   |  22 ++
>  drivers/gpu/drm/panthor/panthor_mmu.h   |   6 +
>  drivers/gpu/drm/panthor/panthor_sched.c |  51 +++-
>  drivers/gpu/drm/panthor/panthor_sched.h |  10 +
>  include/uapi/drm/panthor_drm.h          | 124 ++++++++
>  9 files changed, 611 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/panthor/panthor_dump.c
>  create mode 100644 drivers/gpu/drm/panthor/panthor_dump.h
> 
> diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
> index 55b40ad07f3b..eeb80d8e8064 100644
> --- a/drivers/gpu/drm/panthor/Kconfig
> +++ b/drivers/gpu/drm/panthor/Kconfig
> @@ -14,6 +14,7 @@ config DRM_PANTHOR
>  	select IOMMU_IO_PGTABLE_LPAE
>  	select IOMMU_SUPPORT
>  	select PM_DEVFREQ
> +	select WANT_DEVCOREDUMP
>  	help
>  	  DRM driver for ARM Mali CSF-based GPUs.
>  
> diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
> index 15294719b09c..19be24ddf577 100644
> --- a/drivers/gpu/drm/panthor/Makefile
> +++ b/drivers/gpu/drm/panthor/Makefile
> @@ -4,6 +4,7 @@ panthor-y := \
>  	panthor_devfreq.o \
>  	panthor_device.o \
>  	panthor_drv.o \
> +	panthor_dump.o \
>  	panthor_fw.o \
>  	panthor_gem.o \
>  	panthor_gpu.o \
> diff --git a/drivers/gpu/drm/panthor/panthor_dump.c b/drivers/gpu/drm/panthor/panthor_dump.c
> new file mode 100644
> index 000000000000..7ec0e21dc7e9
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_dump.c
> @@ -0,0 +1,376 @@
> +// SPDX-License-Identifier: GPL-2.0 or MIT
> +/* SPDX-FileCopyrightText: Copyright Collabora 2024 */
> +
> +#include <drm/drm_gem.h>
> +#include <linux/iosys-map.h>
> +#include <linux/devcoredump.h>
> +#include <linux/err.h>
> +#include <linux/vmalloc.h>
> +#include <linux/types.h>
> +#include <uapi/drm/panthor_drm.h>
> +
> +#include "panthor_device.h"
> +#include "panthor_dump.h"
> +#include "panthor_mmu.h"
> +#include "panthor_sched.h"
> +
> +/* A magic value used when starting a new section in the dump */
> +#define PANT_DUMP_MAGIC 0x544e4150 /* PANT */
> +#define PANT_DUMP_MAJOR 1
> +#define PANT_DUMP_MINOR 0
> +
> +/* keep track of where we are in the underlying buffer */
> +struct dump_allocator {
> +	u8 *start;
> +	u8 *curr;
> +	size_t pos;
> +	size_t capacity;
> +};
> +
> +struct vm_dump_count {
> +	u64 size;
> +	u32 vas;
> +};
> +
> +struct queue_count {
> +	u32 queues;
> +};
> +
> +struct dump_group_args {
> +	struct panthor_device *ptdev;
> +	struct dump_allocator *alloc;
> +	struct panthor_group *group;
> +};
> +
> +struct dump_va_args {
> +	struct panthor_device *ptdev;
> +	struct dump_allocator *alloc;
> +};
> +
> +static void *alloc_bytes(struct dump_allocator *alloc, size_t size)
> +{
> +	void *ret;
> +
> +	if (alloc->pos + size > alloc->capacity)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = alloc->curr;
> +	alloc->curr += size;
> +	alloc->pos += size;
> +	return ret;
> +}
> +
> +static struct drm_panthor_dump_header *
> +alloc_header(struct dump_allocator *alloc, u32 type, size_t size)
> +{
> +	struct drm_panthor_dump_header *hdr;
> +	int header_size = sizeof(*hdr);
> +
> +	hdr = alloc_bytes(alloc, header_size);
> +	if (IS_ERR(hdr))
> +		return hdr;
> +
> +	hdr->magic = PANT_DUMP_MAGIC;
> +	hdr->header_type = type;
> +	hdr->header_size = header_size;
> +	hdr->data_size = size;
> +	return hdr;
> +}
> +
> +static int dump_bo(struct panthor_device *ptdev, u8 *dst,
> +		   struct drm_gem_object *obj, int offset, int size)
> +{
> +	struct iosys_map map = {};
> +	int ret;
> +
> +	ret = drm_gem_vmap_unlocked(obj, &map);
> +	if (ret)
> +		return ret;
> +
> +	drm_dbg(&ptdev->base, "dumping bo %p, offset %d, size %d\n", obj,
> +		offset, size);
> +
> +	memcpy(dst, map.vaddr + offset, size);
> +	drm_gem_vunmap_unlocked(obj, &map);
> +	return ret;
> +}
> +
> +static int dump_va(struct dump_va_args *dump_va_args,
> +		   const struct drm_gpuva *va, int type)
> +{
> +	struct drm_gem_object *obj = va->gem.obj;
> +	const int hdr_size =
> +		sizeof(struct drm_panthor_dump_gpuva) + va->va.range;
> +	struct drm_panthor_dump_gpuva *dump_va;
> +	struct drm_panthor_dump_header *dump_hdr;
> +	u8 *bo_data;
> +
> +	dump_hdr = alloc_header(dump_va_args->alloc, type, hdr_size);
> +	if (IS_ERR(dump_hdr))
> +		return PTR_ERR(dump_hdr);
> +
> +	dump_va = alloc_bytes(dump_va_args->alloc, sizeof(*dump_va));
> +	if (IS_ERR(dump_va))
> +		return PTR_ERR(dump_va);
> +
> +	bo_data = alloc_bytes(dump_va_args->alloc, va->va.range);
> +	if (IS_ERR(bo_data))
> +		return PTR_ERR(bo_data);
> +
> +	dump_va->addr = va->va.addr;
> +	dump_va->range = va->va.range;
> +
> +	return dump_bo(dump_va_args->ptdev, bo_data, obj, va->gem.offset,
> +		       va->va.range);
> +}
> +
> +static int dump_va_cb(void *priv, const struct drm_gpuva *va)
> +{
> +	struct dump_va_args *dump_va_args = priv;
> +	int ret;
> +
> +	ret = dump_va(dump_va_args, va, DRM_PANTHOR_DUMP_HEADER_TYPE_VM);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int count_va_cb(void *priv, const struct drm_gpuva *va)
> +{
> +	struct vm_dump_count *count = priv;
> +
> +	count->vas++;
> +	count->size += va->va.range;
> +	return 0;
> +}
> +
> +static void count_queues(struct queue_count *count,
> +			 struct drm_panthor_dump_group_info *info)
> +{
> +	count->queues += info->queue_count;
> +}
> +
> +static int compute_dump_size(struct vm_dump_count *va_count,
> +			     struct queue_count *group_and_q_cnt)
> +{
> +	int size = 0;
> +	int i;
> +
> +	size += sizeof(struct drm_panthor_dump_header);
> +	size += sizeof(struct drm_panthor_dump_version);
> +
> +	size += sizeof(struct drm_panthor_dump_header);
> +	size += sizeof(struct drm_panthor_gpu_info);
> +
> +	size += sizeof(struct drm_panthor_dump_header);
> +	size += sizeof(struct drm_panthor_csif_info);
> +
> +	size += sizeof(struct drm_panthor_dump_header);
> +	size += sizeof(struct drm_panthor_fw_info);
> +
> +	for (i = 0; i < va_count->vas; i++) {
> +		size += sizeof(struct drm_panthor_dump_header);
> +		size += sizeof(struct drm_panthor_dump_gpuva);
> +	}
> +
> +	size += va_count->size;
> +
> +	size += sizeof(struct drm_panthor_dump_header);
> +	size += sizeof(struct drm_panthor_dump_group_info);
> +
> +	for (i = 0; i < group_and_q_cnt->queues; i++) {
> +		size += sizeof(struct drm_panthor_dump_header);
> +		size += sizeof(struct drm_panthor_dump_queue_info);
> +	}
> +
> +	return size;
> +}
> +
> +static int dump_queue_info(struct dump_group_args *dump_group_args,
> +			   struct drm_panthor_dump_queue_info *info)
> +{
> +	struct drm_panthor_dump_header *hdr;
> +	struct drm_panthor_dump_queue_info *queue_info;
> +
> +	drm_dbg(&dump_group_args->ptdev->base,
> +		"dumping queue info for cs_id %d, gpuva: %llx, insert: %llx, extract: %llx\n",
> +		info->cs_id, info->ringbuf_gpuva, info->ringbuf_insert,
> +		info->ringbuf_extract);
> +
> +	hdr = alloc_header(dump_group_args->alloc,
> +			   DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO,
> +			   sizeof(*info));
> +	if (IS_ERR(hdr))
> +		return PTR_ERR(hdr);
> +
> +	queue_info = alloc_bytes(dump_group_args->alloc, sizeof(*queue_info));
> +	if (IS_ERR(queue_info))
> +		return PTR_ERR(queue_info);
> +
> +	*queue_info = *info;
> +	return 0;
> +}
> +
> +static int dump_group_info(struct dump_group_args *dump_group_args,
> +			   struct drm_panthor_dump_group_info *info)
> +{
> +	struct drm_panthor_dump_header *hdr;
> +	struct drm_panthor_dump_group_info *group_info;
> +	int ret = 0;
> +
> +	drm_dbg(&dump_group_args->ptdev->base,
> +		"dumping group info for num_queues: %d, faulty bitmask: %d\n",
> +		info->queue_count, info->faulty_bitmask);
> +
> +	hdr = alloc_header(dump_group_args->alloc,
> +			   DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO,
> +			   sizeof(*info));
> +	if (IS_ERR(hdr))
> +		return PTR_ERR(hdr);
> +
> +	group_info = alloc_bytes(dump_group_args->alloc, sizeof(*group_info));
> +	if (IS_ERR(group_info))
> +		return PTR_ERR(group_info);
> +
> +	*group_info = *info;
> +
> +	for (int i = 0; i < info->queue_count; i++) {
> +		struct drm_panthor_dump_queue_info qinfo;
> +
> +		ret = panthor_sched_get_queueinfo(dump_group_args->group, i,
> +						  &qinfo);
> +		if (ret)
> +			break;
> +		ret = dump_queue_info(dump_group_args, &qinfo);
> +		if (ret)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +int panthor_core_dump(struct panthor_core_dump_args *args)
> +{
> +	u8 *mem;
> +	int dump_size;
> +	int ret = 0;
> +	struct dump_allocator alloc = {};
> +	struct vm_dump_count va_count = {};
> +	struct drm_panthor_dump_header *hdr;
> +	struct drm_panthor_dump_version *version;
> +	struct drm_panthor_gpu_info *gpu_info;
> +	struct drm_panthor_csif_info *csif_info;
> +	struct drm_panthor_fw_info *fw_info;
> +	struct queue_count group_and_q_cnt = {};
> +	struct dump_va_args dump_va_args = {};
> +	struct drm_panthor_dump_group_info group_info;
> +	struct dump_group_args dump_group_args;
> +
> +	panthor_vm_foreach_va(args->group_vm, count_va_cb, &va_count);
> +
> +	panthor_sched_get_groupinfo(args->group, &group_info);
> +
> +	count_queues(&group_and_q_cnt, &group_info);
> +
> +	dump_size = compute_dump_size(&va_count, &group_and_q_cnt);
> +
> +	mem = vzalloc(dump_size);
> +	if (!mem)
> +		return ret;
> +
> +	alloc = (struct dump_allocator){
> +		.start = mem,
> +		.curr = mem,
> +		.pos = 0,
> +		.capacity = dump_size,
> +	};
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_VERSION,
> +			   sizeof(struct drm_panthor_dump_version));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	version = alloc_bytes(&alloc, sizeof(*version));
> +	if (IS_ERR(version)) {
> +		ret = PTR_ERR(version);
> +		goto free_valloc;
> +	}
> +
> +	*version = (struct drm_panthor_dump_version){
> +		.major = PANT_DUMP_MAJOR,
> +		.minor = PANT_DUMP_MINOR,
> +	};
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO,
> +			   sizeof(args->ptdev->gpu_info));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	gpu_info = alloc_bytes(&alloc, sizeof(*gpu_info));
> +	if (IS_ERR(gpu_info)) {
> +		ret = PTR_ERR(gpu_info);
> +		goto free_valloc;
> +	}
> +
> +	*gpu_info = args->ptdev->gpu_info;
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO,
> +			   sizeof(args->ptdev->csif_info));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	csif_info = alloc_bytes(&alloc, sizeof(*csif_info));
> +	if (IS_ERR(csif_info)) {
> +		ret = PTR_ERR(csif_info);
> +		goto free_valloc;
> +	}
> +
> +	*csif_info = args->ptdev->csif_info;
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO,
> +			   sizeof(args->ptdev->fw_info));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	fw_info = alloc_bytes(&alloc, sizeof(*fw_info));
> +	if (IS_ERR(fw_info)) {
> +		ret = PTR_ERR(fw_info);
> +		goto free_valloc;
> +	}
> +
> +	*fw_info = args->ptdev->fw_info;
> +
> +	dump_va_args.ptdev = args->ptdev;
> +	dump_va_args.alloc = &alloc;
> +	ret = panthor_vm_foreach_va(args->group_vm, dump_va_cb, &dump_va_args);
> +	if (ret)
> +		goto free_valloc;
> +
> +	dump_group_args =
> +		(struct dump_group_args){ args->ptdev, &alloc, args->group };
> +	panthor_sched_get_groupinfo(args->group, &group_info);
> +	dump_group_info(&dump_group_args, &group_info);
> +
> +	if (alloc.pos < dump_size)
> +		drm_warn(&args->ptdev->base,
> +			 "dump size mismatch: expected %d, got %zu\n",
> +			 dump_size, alloc.pos);
> +
> +	dev_coredumpv(args->ptdev->base.dev, alloc.start, alloc.pos,
> +		      GFP_KERNEL);
> +
> +	return ret;
> +
> +free_valloc:
> +	vfree(mem);
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/panthor/panthor_dump.h b/drivers/gpu/drm/panthor/panthor_dump.h
> new file mode 100644
> index 000000000000..2a02943a2dbd
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_dump.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 or MIT */
> +/* SPDX-FileCopyrightText: Copyright Collabora 2024 */
> +
> +#ifndef __PANTHOR_DUMP_H__
> +#define __PANTHOR_DUMP_H__
> +
> +#include <drm/drm_gpuvm.h>
> +#include <drm/panthor_drm.h>
> +
> +#include "panthor_device.h"
> +#include "panthor_gem.h"
> +
> +struct panthor_core_dump_args {
> +	struct panthor_device *ptdev;
> +	struct panthor_vm *group_vm;
> +	struct panthor_group *group;
> +};
> +
> +int panthor_core_dump(struct panthor_core_dump_args *args);
> +
> +#endif /* __PANTHOR_DUMP_H__ */
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index 412e95fcfb92..61d61157ace0 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -2632,6 +2632,28 @@ int panthor_vm_prepare_mapped_bos_resvs(struct drm_exec *exec, struct panthor_vm
>  	return drm_gpuvm_prepare_objects(&vm->base, exec, slot_count);
>  }
>  
> +/**
> + * panthor_vm_foreachva() - Execute a callback for each VA in a VM
> + *
> + */
> +int panthor_vm_foreach_va(struct panthor_vm *vm,
> +			  int (*cb)(void *priv, const struct drm_gpuva *va),
> +			  void *priv)
> +{
> +	struct drm_gpuva *va;
> +	int ret = 0;
> +
> +	mutex_lock(&vm->op_lock);
> +	drm_gpuvm_for_each_va(va, &vm->base) {
> +		ret = cb(priv, va);
> +		if (ret)
> +			break;
> +	}
> +	mutex_unlock(&vm->op_lock);
> +
> +	return ret;
> +}
> +
>  /**
>   * panthor_mmu_unplug() - Unplug the MMU logic
>   * @ptdev: Device.
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h
> index 6788771071e3..05a5d68b23ae 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.h
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.h
> @@ -8,6 +8,7 @@
>  #include <linux/dma-resv.h>
>  
>  struct drm_exec;
> +struct drm_gpuva;
>  struct drm_sched_job;
>  struct panthor_gem_object;
>  struct panthor_heap_pool;
> @@ -52,6 +53,11 @@ void panthor_vm_add_job_fence_to_bos_resvs(struct panthor_vm *vm,
>  					   struct drm_sched_job *job);
>  
>  struct dma_resv *panthor_vm_resv(struct panthor_vm *vm);
> +
> +int panthor_vm_foreach_va(struct panthor_vm *vm,
> +			  int (*cb)(void *priv, const struct drm_gpuva *va),
> +			  void *priv);
> +
>  struct drm_gem_object *panthor_vm_root_gem(struct panthor_vm *vm);
>  
>  void panthor_vm_pool_destroy(struct panthor_file *pfile);
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index e0ecc8bcfaae..59c30b311ee9 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
> @@ -24,6 +24,7 @@
>  
>  #include "panthor_devfreq.h"
>  #include "panthor_device.h"
> +#include "panthor_dump.h"
>  #include "panthor_fw.h"
>  #include "panthor_gem.h"
>  #include "panthor_gpu.h"
> @@ -2805,6 +2806,45 @@ static void group_sync_upd_work(struct work_struct *work)
>  	group_put(group);
>  }
>  
> +/**
> + * panthor_sched_get_groupinfo() - Build a group info structure for the group
> + *
> + * @group: the group to build a group info structure for
> + * @info: the group info structure to fill
> + */
> +void panthor_sched_get_groupinfo(struct panthor_group *group,
> +				 struct drm_panthor_dump_group_info *info)
> +{
> +	info->queue_count = group->queue_count;
> +	info->faulty_bitmask = group->fatal_queues;
> +}
> +
> +/** panthor_sched_get_queueinfo() - Build a queue info structure for a queue
> + * given its group and its cs_id
> + *
> + * @group: the group the queue belongs to
> + * @cs_id: the command stream ID of the queue
> + * @info: the queue info structure to fill
> + */
> +int panthor_sched_get_queueinfo(struct panthor_group *group, u32 cs_id,
> +				struct drm_panthor_dump_queue_info *info)
> +{
> +	struct panthor_queue *queue;
> +
> +	if (cs_id >= group->queue_count)
> +		return -EINVAL;
> +
> +	queue = group->queues[cs_id];
> +
> +	info->cs_id = cs_id;
> +	info->ringbuf_insert = queue->iface.input->insert;
> +	info->ringbuf_extract = queue->iface.output->extract;
> +	info->ringbuf_gpuva = panthor_kernel_bo_gpuva(queue->ringbuf);
> +	info->ringbuf_size = panthor_kernel_bo_size(queue->ringbuf);
> +
> +	return 0;
> +}
> +
>  static struct dma_fence *
>  queue_run_job(struct drm_sched_job *sched_job)
>  {
> @@ -2946,7 +2986,7 @@ queue_timedout_job(struct drm_sched_job *sched_job)
>  	struct panthor_device *ptdev = group->ptdev;
>  	struct panthor_scheduler *sched = ptdev->scheduler;
>  	struct panthor_queue *queue = group->queues[job->queue_idx];
> -
> +	struct panthor_core_dump_args core_dump_args;
>  	drm_warn(&ptdev->base, "job timeout\n");
>  
>  	drm_WARN_ON(&ptdev->base, atomic_read(&sched->reset.in_progress));
> @@ -2955,6 +2995,15 @@ queue_timedout_job(struct drm_sched_job *sched_job)
>  
>  	mutex_lock(&sched->lock);
>  	group->timedout = true;
> +
> +	core_dump_args = (struct panthor_core_dump_args) {
> +		.ptdev = ptdev,
> +		.group_vm = job->group->vm,
> +		.group = job->group,
> +	};
> +
> +	panthor_core_dump(&core_dump_args);
> +
>  	if (group->csg_id >= 0) {
>  		sched_queue_delayed_work(ptdev->scheduler, tick, 0);
>  	} else {
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
> index 3a30d2328b30..9a5b53498dcc 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.h
> +++ b/drivers/gpu/drm/panthor/panthor_sched.h
> @@ -17,6 +17,9 @@ struct panthor_device;
>  struct panthor_file;
>  struct panthor_group_pool;
>  struct panthor_job;
> +struct panthor_group;
> +struct drm_panthor_dump_group_info;
> +struct drm_panthor_dump_queue_info;
>  
>  int panthor_group_create(struct panthor_file *pfile,
>  			 const struct drm_panthor_group_create *group_args,
> @@ -41,6 +44,13 @@ int panthor_sched_init(struct panthor_device *ptdev);
>  void panthor_sched_unplug(struct panthor_device *ptdev);
>  void panthor_sched_pre_reset(struct panthor_device *ptdev);
>  void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed);
> +
> +void panthor_sched_get_groupinfo(struct panthor_group *group,
> +				 struct drm_panthor_dump_group_info *info);
> +
> +int panthor_sched_get_queueinfo(struct panthor_group *group, u32 cs_id,
> +				struct drm_panthor_dump_queue_info *info);
> +
>  void panthor_sched_suspend(struct panthor_device *ptdev);
>  void panthor_sched_resume(struct panthor_device *ptdev);
>  
> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
> index e235cf452460..82ec0f20c49e 100644
> --- a/include/uapi/drm/panthor_drm.h
> +++ b/include/uapi/drm/panthor_drm.h
> @@ -969,6 +969,130 @@ struct drm_panthor_tiler_heap_destroy {
>  	__u32 pad;
>  };
>  
> +/**
> + * enum drm_panthor_dump_header_type - Identifies the type of data that follows
> + * in a panthor core dump.
> + */
> +enum drm_panthor_dump_header_type {
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_VERSION = 0,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO: Gpu information.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO = 1,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO: Command stream interface information.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO = 2,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO: Information about the firmware.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO = 3,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_VM: A dump of the VM for the context.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_VM = 4,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO: Describes a group. A dump can
> +	 * contain either the faulty group, or all groups for the DRM FD.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO = 5,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO: Describes a faulty queue. This
> +	 * will immediately follow a group info.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO = 6,
> +};
> +
> +/**
> + * struct drm_panthor_dump_header - A header that describes a section of a panthor core dump.
> + */
> +struct drm_panthor_dump_header {
> +	/** @magic: Always set to PANT (0x544e4150). */
> +	__u32 magic;
> +
> +	/** @header_type: Identifies the type of data in the following section of the
> +	 * core dump file
> +	 */
> +	enum drm_panthor_dump_header_type header_type;
> +
> +	/** @header_size: The size of the header.
> +	 *
> +	 * This is for backward-compatibility purposes in case this structure is
> +	 * augmented in the future. It allows userspace to skip over the header and
> +	 * access the actual data it describes.
> +	 */
> +	__u32 header_size;
> +
> +	/** @data_size: The size of the following section */
> +	__u32 data_size;
> +};
> +
> +/**
> + * struct drm_panthor_dump_version - Version information for a Panthor GPU dump.
> + *
> + * This structure is used to hold version information when performing a dump of
> + * the state of a Panthor GPU.
> + */
> +struct drm_panthor_dump_version {
> +	/** @major: Versioning information for backwards compatibility */
> +	__u32 major;
> +	/** @minor: Versioning information for backwards compatibility */
> +	__u32 minor;
> +};
> +
> +/**
> + * struct drm_panthor_dump_group_info - Group information for a Panthor GPU
> + * dump.
> + *
> + * This structure is used to hold information about a group when performing a
> + * dump of the state of a Panthor GPU.
> + */
> +struct drm_panthor_dump_group_info {
> +	/** @queue_count: The number of queues in the group. */
> +	__u32 queue_count;
> +	/** @faulty_queues: A bitmask denoting the faulty queues */
> +	__u32 faulty_bitmask;
> +};
> +
> +#define DRM_PANTHOR_DUMP_QUEUE_INFO_FLAGS_FAULTY	(1 << 0)
> +
> +/**
> + * struct drm_panthor_dump_queue_info - Queue information for a Panthor GPU
> + * dump.
> + *
> + * This structure is used to hold information about a queue when performing a
> + * dump of the state of a Panthor GPU.
> + */
> +struct drm_panthor_dump_queue_info {
> +	/** See DRM_PANTHOR_DUMP_QUEUE_INFO_FLAGS_XXX */
> +	u32 flags;
> +	/** @cs_id: The ID of the command stream. */
> +	__s32 cs_id;
> +	/** @faulty: Whether this queue has faulted */
> +	/** @ringbuf_gpuva: The GPU virtual address of the ring buffer. */
> +	__u64 ringbuf_gpuva;
> +	/** @ringbuf_insert: The insert point (i.e.: offset) in the ring buffer. This
> +	 * is where a instruction would be inserted next by the CPU.
> +	 */
> +	__u64 ringbuf_insert;
> +	/** @ringbuf_extract: The extract point (i.e.: offset) in the ring buffer.
> +	 * This is where the GPU would read the next instruction.
> +	 */
> +	__u64 ringbuf_extract;
> +	/** @ringbuf_size: The size of the ring buffer */
> +	__u64 ringbuf_size;
> +};
> +
> +/**
> + * struct drm_panthor_dump_gpuva - Describes a GPU VA range in the dump.
> + */
> +struct drm_panthor_dump_gpuva {
> +	/** @addr: The start address for the mapping */
> +	__u64 addr;
> +	/** @range: The range covered by the VA mapping */
> +	__u64 range;
> +};
> +
>  #if defined(__cplusplus)
>  }
>  #endif
> -- 
> 2.45.2
Boris Brezillon Aug. 23, 2024, 2:46 p.m. UTC | #2
Hi Daniel

On Wed, 21 Aug 2024 11:37:28 -0300
Daniel Almeida <daniel.almeida@collabora.com> wrote:

[...]

> +static void *alloc_bytes(struct dump_allocator *alloc, size_t size)
> +{
> +	void *ret;
> +
> +	if (alloc->pos + size > alloc->capacity)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = alloc->curr;

Hm, I suspect we might want to enforce some kind of alignment to make
sure things can be directly dereferenced without having to copy stuff.

> +	alloc->curr += size;
> +	alloc->pos += size;
> +	return ret;
> +}
> +
> +static struct drm_panthor_dump_header *
> +alloc_header(struct dump_allocator *alloc, u32 type, size_t size)
> +{
> +	struct drm_panthor_dump_header *hdr;
> +	int header_size = sizeof(*hdr);
> +
> +	hdr = alloc_bytes(alloc, header_size);
> +	if (IS_ERR(hdr))
> +		return hdr;
> +
> +	hdr->magic = PANT_DUMP_MAGIC;
> +	hdr->header_type = type;
> +	hdr->header_size = header_size;
> +	hdr->data_size = size;
> +	return hdr;
> +}
> +
> +static int dump_bo(struct panthor_device *ptdev, u8 *dst,
> +		   struct drm_gem_object *obj, int offset, int size)
> +{
> +	struct iosys_map map = {};
> +	int ret;
> +
> +	ret = drm_gem_vmap_unlocked(obj, &map);

This drm_gem_vmap_unlocked() call will be problematic as soon as you
call the dump logic from any of the scheduler work which are part of
the dma fence signalling path (see [1] for more details). TLDR; in this
path you're not allowed to block on a dma_resv_lock(), which
drm_gem_vmap_unlocked() does. You also can't call the locked variant,
otherwise you're breaking the lock_held assumption.

I had a quick look at the Xe driver which has a similar architecture
and implements devcoredump, and they do the dumping in 2 steps to
work around this:

1. In the fault path, they collect VA regions and their associated BOs
(they call that a VM snapshot) and a bunch of other information you
only have at fault time (other kind of snapshots) and might disappear if
you don't save them somewhere. All allocations in this path are done
with GFP_NOWAIT (see below for an explanation). They then use
dev_coredumpm() instead of dev_coredumpv(), so they don't have to
allocate memory for the final dump, and instead stream the dump when
userspace reads the core file.

2. In their xe_devcoredump_read() function, they can dump the BO content
because we're allowed to take the resv lock in that path. Not to
mention we no longer duplicate the BO data: it just leaves in the
original BO and is streamed when userspace reads the coredump file.

> +	if (ret)
> +		return ret;
> +
> +	drm_dbg(&ptdev->base, "dumping bo %p, offset %d, size %d\n", obj,
> +		offset, size);
> +
> +	memcpy(dst, map.vaddr + offset, size);
> +	drm_gem_vunmap_unlocked(obj, &map);
> +	return ret;
> +}
> +

[...]

> +
> +int panthor_core_dump(struct panthor_core_dump_args *args)
> +{
> +	u8 *mem;
> +	int dump_size;
> +	int ret = 0;
> +	struct dump_allocator alloc = {};
> +	struct vm_dump_count va_count = {};
> +	struct drm_panthor_dump_header *hdr;
> +	struct drm_panthor_dump_version *version;
> +	struct drm_panthor_gpu_info *gpu_info;
> +	struct drm_panthor_csif_info *csif_info;
> +	struct drm_panthor_fw_info *fw_info;
> +	struct queue_count group_and_q_cnt = {};
> +	struct dump_va_args dump_va_args = {};
> +	struct drm_panthor_dump_group_info group_info;
> +	struct dump_group_args dump_group_args;
> +
> +	panthor_vm_foreach_va(args->group_vm, count_va_cb, &va_count);
> +
> +	panthor_sched_get_groupinfo(args->group, &group_info);
> +
> +	count_queues(&group_and_q_cnt, &group_info);
> +
> +	dump_size = compute_dump_size(&va_count, &group_and_q_cnt);
> +
> +	mem = vzalloc(dump_size);

The dumper will be called in a path where it can't block on allocation,
because blocking/non-failable allocations might trigger the future
panthor shrinker that might in turn wait on fences that can't be
signalled because we're blocked waiting on devcoredump to complete its
dump.

You should use kvzalloc(GFP_NOWAIT) in this path.

> +	if (!mem)
> +		return ret;
> +
> +	alloc = (struct dump_allocator){
> +		.start = mem,
> +		.curr = mem,
> +		.pos = 0,
> +		.capacity = dump_size,
> +	};
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_VERSION,
> +			   sizeof(struct drm_panthor_dump_version));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	version = alloc_bytes(&alloc, sizeof(*version));
> +	if (IS_ERR(version)) {
> +		ret = PTR_ERR(version);
> +		goto free_valloc;
> +	}
> +
> +	*version = (struct drm_panthor_dump_version){
> +		.major = PANT_DUMP_MAJOR,
> +		.minor = PANT_DUMP_MINOR,
> +	};
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO,
> +			   sizeof(args->ptdev->gpu_info));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	gpu_info = alloc_bytes(&alloc, sizeof(*gpu_info));
> +	if (IS_ERR(gpu_info)) {
> +		ret = PTR_ERR(gpu_info);
> +		goto free_valloc;
> +	}
> +
> +	*gpu_info = args->ptdev->gpu_info;
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO,
> +			   sizeof(args->ptdev->csif_info));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	csif_info = alloc_bytes(&alloc, sizeof(*csif_info));
> +	if (IS_ERR(csif_info)) {
> +		ret = PTR_ERR(csif_info);
> +		goto free_valloc;
> +	}
> +
> +	*csif_info = args->ptdev->csif_info;
> +
> +	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO,
> +			   sizeof(args->ptdev->fw_info));
> +	if (IS_ERR(hdr)) {
> +		ret = PTR_ERR(hdr);
> +		goto free_valloc;
> +	}
> +
> +	fw_info = alloc_bytes(&alloc, sizeof(*fw_info));
> +	if (IS_ERR(fw_info)) {
> +		ret = PTR_ERR(fw_info);
> +		goto free_valloc;
> +	}
> +
> +	*fw_info = args->ptdev->fw_info;
> +
> +	dump_va_args.ptdev = args->ptdev;
> +	dump_va_args.alloc = &alloc;
> +	ret = panthor_vm_foreach_va(args->group_vm, dump_va_cb, &dump_va_args);
> +	if (ret)
> +		goto free_valloc;
> +
> +	dump_group_args =
> +		(struct dump_group_args){ args->ptdev, &alloc, args->group };
> +	panthor_sched_get_groupinfo(args->group, &group_info);
> +	dump_group_info(&dump_group_args, &group_info);
> +
> +	if (alloc.pos < dump_size)
> +		drm_warn(&args->ptdev->base,
> +			 "dump size mismatch: expected %d, got %zu\n",
> +			 dump_size, alloc.pos);
> +
> +	dev_coredumpv(args->ptdev->base.dev, alloc.start, alloc.pos,
> +		      GFP_KERNEL);
> +
> +	return ret;
> +
> +free_valloc:
> +	vfree(mem);
> +	return ret;
> +}

[...]

> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
> index e235cf452460..82ec0f20c49e 100644
> --- a/include/uapi/drm/panthor_drm.h
> +++ b/include/uapi/drm/panthor_drm.h
> @@ -969,6 +969,130 @@ struct drm_panthor_tiler_heap_destroy {
>  	__u32 pad;
>  };
>  
> +/**
> + * enum drm_panthor_dump_header_type - Identifies the type of data that follows
> + * in a panthor core dump.
> + */
> +enum drm_panthor_dump_header_type {
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_VERSION = 0,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO: Gpu information.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO = 1,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO: Command stream interface information.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO = 2,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO: Information about the firmware.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO = 3,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_VM: A dump of the VM for the context.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_VM = 4,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO: Describes a group. A dump can
> +	 * contain either the faulty group, or all groups for the DRM FD.

Let's decide on one. Given getting back to a drm_file from a faulty job
is not easy, I think we should focus on dumping the faulty group only
for now.

> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO = 5,
> +	/**
> +	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO: Describes a faulty queue. This
> +	 * will immediately follow a group info.
> +	 */
> +	DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO = 6,

Given a group has a maximum of 32 queues (see MAX_CS_PER_CSG), I'm not
sure we should split the group and queue info into 2 different
sections. Just add a

	struct drm_panthor_dump_queue_info queues[32];

field to drm_panthor_dump_queue_info and you should be good.

> +};
> +
> +/**
> + * struct drm_panthor_dump_header - A header that describes a section of a panthor core dump.
> + */
> +struct drm_panthor_dump_header {

I would call that one dump_section or dump_section_header.

> +	/** @magic: Always set to PANT (0x544e4150). */
> +	__u32 magic;

Not convinced we need to repeat the magic for each header. Having one
in the coredump entry should probably be enough.

> +
> +	/** @header_type: Identifies the type of data in the following section of the

For multiline doc headers, we use the following format:

	/**
	 * @xxx: blabla
	 *
	 * ...

> +	 * core dump file
> +	 */
> +	enum drm_panthor_dump_header_type header_type;
> +
> +	/** @header_size: The size of the header.
> +	 *
> +	 * This is for backward-compatibility purposes in case this structure is
> +	 * augmented in the future. It allows userspace to skip over the header and
> +	 * access the actual data it describes.
> +	 */
> +	__u32 header_size;

Feels like the section itself could embed the extra information needed,
with a new header_type so the old version keeps working. Not convinced
we will ever need anything more in the header that couldn't be
expressed through other means to be honest. There's one interesting
purpose for this field though: enforcing alignment of the following
data. Another way of doing that would be to split the headers and
content, and have the headers provide an explicit data_offset.

> +
> +	/** @data_size: The size of the following section */
> +	__u32 data_size;

If we want to make that future-proof, we should probably use an u64
here.

> +};
> +
> +/**
> + * struct drm_panthor_dump_version - Version information for a Panthor GPU dump.
> + *
> + * This structure is used to hold version information when performing a dump of
> + * the state of a Panthor GPU.
> + */
> +struct drm_panthor_dump_version {

I would move the magic here and call that one drm_panthor_dump_header.

> +	/** @major: Versioning information for backwards compatibility */
> +	__u32 major;

Please add an blank line between each field definition.

> +	/** @minor: Versioning information for backwards compatibility */
> +	__u32 minor;
> +};
> +
> +/**
> + * struct drm_panthor_dump_group_info - Group information for a Panthor GPU
> + * dump.
> + *
> + * This structure is used to hold information about a group when performing a
> + * dump of the state of a Panthor GPU.
> + */
> +struct drm_panthor_dump_group_info {
> +	/** @queue_count: The number of queues in the group. */
> +	__u32 queue_count;
> +	/** @faulty_queues: A bitmask denoting the faulty queues */
> +	__u32 faulty_bitmask;
> +};
> +
> +#define DRM_PANTHOR_DUMP_QUEUE_INFO_FLAGS_FAULTY	(1 << 0)
> +
> +/**
> + * struct drm_panthor_dump_queue_info - Queue information for a Panthor GPU
> + * dump.
> + *
> + * This structure is used to hold information about a queue when performing a
> + * dump of the state of a Panthor GPU.
> + */
> +struct drm_panthor_dump_queue_info {
> +	/** See DRM_PANTHOR_DUMP_QUEUE_INFO_FLAGS_XXX */
> +	u32 flags;
> +	/** @cs_id: The ID of the command stream. */
> +	__s32 cs_id;
> +	/** @faulty: Whether this queue has faulted */

There's no field defined, just the doc.

> +	/** @ringbuf_gpuva: The GPU virtual address of the ring buffer. */
> +	__u64 ringbuf_gpuva;
> +	/** @ringbuf_insert: The insert point (i.e.: offset) in the ring buffer. This
> +	 * is where a instruction would be inserted next by the CPU.
> +	 */
> +	__u64 ringbuf_insert;
> +	/** @ringbuf_extract: The extract point (i.e.: offset) in the ring buffer.
> +	 * This is where the GPU would read the next instruction.
> +	 */
> +	__u64 ringbuf_extract;

Is it not encoding the current ring buffer position, rather than the
next one? For instance, I would expect us to pass
ringbuf_gpuva + (ringbuf_extract % ringbuf_size) to the userspace
decoder if we want to follow the flow of instructions that lead to the
GPU fault.

> +	/** @ringbuf_size: The size of the ring buffer */
> +	__u64 ringbuf_size;

I think it's also interesting to dump
panthor_fw_cs_output_iface::status_cmd_ptr so we know exactly which CS
instruction was being executed when the crash happened (I can imagine
the faulty instruction being pointed at in pandecode). Actually, I think
pretty much everything in panthor_fw_cs_output_iface is interesting to
have. Beware that most of the information in panthor_fw_cs_output_iface
are only valid after a STATUS_UPDATE or SUSPEND operation, so probably
something to look at when you take the faulty group snapshot.

> +};
> +
> +/**
> + * struct drm_panthor_dump_gpuva - Describes a GPU VA range in the dump.
> + */
> +struct drm_panthor_dump_gpuva {
> +	/** @addr: The start address for the mapping */
> +	__u64 addr;
> +	/** @range: The range covered by the VA mapping */
> +	__u64 range;
> +};
> +
>  #if defined(__cplusplus)
>  }
>  #endif

That's it for now. I didn't focus much on the implementation as I think
the redesign I suggested will significantly change it.

Regards,

Boris

[1]https://elixir.bootlin.com/linux/v6.10.4/source/drivers/dma-buf/dma-fence.c#L195
diff mbox series

Patch

diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
index 55b40ad07f3b..eeb80d8e8064 100644
--- a/drivers/gpu/drm/panthor/Kconfig
+++ b/drivers/gpu/drm/panthor/Kconfig
@@ -14,6 +14,7 @@  config DRM_PANTHOR
 	select IOMMU_IO_PGTABLE_LPAE
 	select IOMMU_SUPPORT
 	select PM_DEVFREQ
+	select WANT_DEVCOREDUMP
 	help
 	  DRM driver for ARM Mali CSF-based GPUs.
 
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index 15294719b09c..19be24ddf577 100644
--- a/drivers/gpu/drm/panthor/Makefile
+++ b/drivers/gpu/drm/panthor/Makefile
@@ -4,6 +4,7 @@  panthor-y := \
 	panthor_devfreq.o \
 	panthor_device.o \
 	panthor_drv.o \
+	panthor_dump.o \
 	panthor_fw.o \
 	panthor_gem.o \
 	panthor_gpu.o \
diff --git a/drivers/gpu/drm/panthor/panthor_dump.c b/drivers/gpu/drm/panthor/panthor_dump.c
new file mode 100644
index 000000000000..7ec0e21dc7e9
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_dump.c
@@ -0,0 +1,376 @@ 
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* SPDX-FileCopyrightText: Copyright Collabora 2024 */
+
+#include <drm/drm_gem.h>
+#include <linux/iosys-map.h>
+#include <linux/devcoredump.h>
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <uapi/drm/panthor_drm.h>
+
+#include "panthor_device.h"
+#include "panthor_dump.h"
+#include "panthor_mmu.h"
+#include "panthor_sched.h"
+
+/* A magic value used when starting a new section in the dump */
+#define PANT_DUMP_MAGIC 0x544e4150 /* PANT */
+#define PANT_DUMP_MAJOR 1
+#define PANT_DUMP_MINOR 0
+
+/* keep track of where we are in the underlying buffer */
+struct dump_allocator {
+	u8 *start;
+	u8 *curr;
+	size_t pos;
+	size_t capacity;
+};
+
+struct vm_dump_count {
+	u64 size;
+	u32 vas;
+};
+
+struct queue_count {
+	u32 queues;
+};
+
+struct dump_group_args {
+	struct panthor_device *ptdev;
+	struct dump_allocator *alloc;
+	struct panthor_group *group;
+};
+
+struct dump_va_args {
+	struct panthor_device *ptdev;
+	struct dump_allocator *alloc;
+};
+
+static void *alloc_bytes(struct dump_allocator *alloc, size_t size)
+{
+	void *ret;
+
+	if (alloc->pos + size > alloc->capacity)
+		return ERR_PTR(-ENOMEM);
+
+	ret = alloc->curr;
+	alloc->curr += size;
+	alloc->pos += size;
+	return ret;
+}
+
+static struct drm_panthor_dump_header *
+alloc_header(struct dump_allocator *alloc, u32 type, size_t size)
+{
+	struct drm_panthor_dump_header *hdr;
+	int header_size = sizeof(*hdr);
+
+	hdr = alloc_bytes(alloc, header_size);
+	if (IS_ERR(hdr))
+		return hdr;
+
+	hdr->magic = PANT_DUMP_MAGIC;
+	hdr->header_type = type;
+	hdr->header_size = header_size;
+	hdr->data_size = size;
+	return hdr;
+}
+
+static int dump_bo(struct panthor_device *ptdev, u8 *dst,
+		   struct drm_gem_object *obj, int offset, int size)
+{
+	struct iosys_map map = {};
+	int ret;
+
+	ret = drm_gem_vmap_unlocked(obj, &map);
+	if (ret)
+		return ret;
+
+	drm_dbg(&ptdev->base, "dumping bo %p, offset %d, size %d\n", obj,
+		offset, size);
+
+	memcpy(dst, map.vaddr + offset, size);
+	drm_gem_vunmap_unlocked(obj, &map);
+	return ret;
+}
+
+static int dump_va(struct dump_va_args *dump_va_args,
+		   const struct drm_gpuva *va, int type)
+{
+	struct drm_gem_object *obj = va->gem.obj;
+	const int hdr_size =
+		sizeof(struct drm_panthor_dump_gpuva) + va->va.range;
+	struct drm_panthor_dump_gpuva *dump_va;
+	struct drm_panthor_dump_header *dump_hdr;
+	u8 *bo_data;
+
+	dump_hdr = alloc_header(dump_va_args->alloc, type, hdr_size);
+	if (IS_ERR(dump_hdr))
+		return PTR_ERR(dump_hdr);
+
+	dump_va = alloc_bytes(dump_va_args->alloc, sizeof(*dump_va));
+	if (IS_ERR(dump_va))
+		return PTR_ERR(dump_va);
+
+	bo_data = alloc_bytes(dump_va_args->alloc, va->va.range);
+	if (IS_ERR(bo_data))
+		return PTR_ERR(bo_data);
+
+	dump_va->addr = va->va.addr;
+	dump_va->range = va->va.range;
+
+	return dump_bo(dump_va_args->ptdev, bo_data, obj, va->gem.offset,
+		       va->va.range);
+}
+
+static int dump_va_cb(void *priv, const struct drm_gpuva *va)
+{
+	struct dump_va_args *dump_va_args = priv;
+	int ret;
+
+	ret = dump_va(dump_va_args, va, DRM_PANTHOR_DUMP_HEADER_TYPE_VM);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int count_va_cb(void *priv, const struct drm_gpuva *va)
+{
+	struct vm_dump_count *count = priv;
+
+	count->vas++;
+	count->size += va->va.range;
+	return 0;
+}
+
+static void count_queues(struct queue_count *count,
+			 struct drm_panthor_dump_group_info *info)
+{
+	count->queues += info->queue_count;
+}
+
+static int compute_dump_size(struct vm_dump_count *va_count,
+			     struct queue_count *group_and_q_cnt)
+{
+	int size = 0;
+	int i;
+
+	size += sizeof(struct drm_panthor_dump_header);
+	size += sizeof(struct drm_panthor_dump_version);
+
+	size += sizeof(struct drm_panthor_dump_header);
+	size += sizeof(struct drm_panthor_gpu_info);
+
+	size += sizeof(struct drm_panthor_dump_header);
+	size += sizeof(struct drm_panthor_csif_info);
+
+	size += sizeof(struct drm_panthor_dump_header);
+	size += sizeof(struct drm_panthor_fw_info);
+
+	for (i = 0; i < va_count->vas; i++) {
+		size += sizeof(struct drm_panthor_dump_header);
+		size += sizeof(struct drm_panthor_dump_gpuva);
+	}
+
+	size += va_count->size;
+
+	size += sizeof(struct drm_panthor_dump_header);
+	size += sizeof(struct drm_panthor_dump_group_info);
+
+	for (i = 0; i < group_and_q_cnt->queues; i++) {
+		size += sizeof(struct drm_panthor_dump_header);
+		size += sizeof(struct drm_panthor_dump_queue_info);
+	}
+
+	return size;
+}
+
+static int dump_queue_info(struct dump_group_args *dump_group_args,
+			   struct drm_panthor_dump_queue_info *info)
+{
+	struct drm_panthor_dump_header *hdr;
+	struct drm_panthor_dump_queue_info *queue_info;
+
+	drm_dbg(&dump_group_args->ptdev->base,
+		"dumping queue info for cs_id %d, gpuva: %llx, insert: %llx, extract: %llx\n",
+		info->cs_id, info->ringbuf_gpuva, info->ringbuf_insert,
+		info->ringbuf_extract);
+
+	hdr = alloc_header(dump_group_args->alloc,
+			   DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO,
+			   sizeof(*info));
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	queue_info = alloc_bytes(dump_group_args->alloc, sizeof(*queue_info));
+	if (IS_ERR(queue_info))
+		return PTR_ERR(queue_info);
+
+	*queue_info = *info;
+	return 0;
+}
+
+static int dump_group_info(struct dump_group_args *dump_group_args,
+			   struct drm_panthor_dump_group_info *info)
+{
+	struct drm_panthor_dump_header *hdr;
+	struct drm_panthor_dump_group_info *group_info;
+	int ret = 0;
+
+	drm_dbg(&dump_group_args->ptdev->base,
+		"dumping group info for num_queues: %d, faulty bitmask: %d\n",
+		info->queue_count, info->faulty_bitmask);
+
+	hdr = alloc_header(dump_group_args->alloc,
+			   DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO,
+			   sizeof(*info));
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	group_info = alloc_bytes(dump_group_args->alloc, sizeof(*group_info));
+	if (IS_ERR(group_info))
+		return PTR_ERR(group_info);
+
+	*group_info = *info;
+
+	for (int i = 0; i < info->queue_count; i++) {
+		struct drm_panthor_dump_queue_info qinfo;
+
+		ret = panthor_sched_get_queueinfo(dump_group_args->group, i,
+						  &qinfo);
+		if (ret)
+			break;
+		ret = dump_queue_info(dump_group_args, &qinfo);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+int panthor_core_dump(struct panthor_core_dump_args *args)
+{
+	u8 *mem;
+	int dump_size;
+	int ret = 0;
+	struct dump_allocator alloc = {};
+	struct vm_dump_count va_count = {};
+	struct drm_panthor_dump_header *hdr;
+	struct drm_panthor_dump_version *version;
+	struct drm_panthor_gpu_info *gpu_info;
+	struct drm_panthor_csif_info *csif_info;
+	struct drm_panthor_fw_info *fw_info;
+	struct queue_count group_and_q_cnt = {};
+	struct dump_va_args dump_va_args = {};
+	struct drm_panthor_dump_group_info group_info;
+	struct dump_group_args dump_group_args;
+
+	panthor_vm_foreach_va(args->group_vm, count_va_cb, &va_count);
+
+	panthor_sched_get_groupinfo(args->group, &group_info);
+
+	count_queues(&group_and_q_cnt, &group_info);
+
+	dump_size = compute_dump_size(&va_count, &group_and_q_cnt);
+
+	mem = vzalloc(dump_size);
+	if (!mem)
+		return ret;
+
+	alloc = (struct dump_allocator){
+		.start = mem,
+		.curr = mem,
+		.pos = 0,
+		.capacity = dump_size,
+	};
+
+	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_VERSION,
+			   sizeof(struct drm_panthor_dump_version));
+	if (IS_ERR(hdr)) {
+		ret = PTR_ERR(hdr);
+		goto free_valloc;
+	}
+
+	version = alloc_bytes(&alloc, sizeof(*version));
+	if (IS_ERR(version)) {
+		ret = PTR_ERR(version);
+		goto free_valloc;
+	}
+
+	*version = (struct drm_panthor_dump_version){
+		.major = PANT_DUMP_MAJOR,
+		.minor = PANT_DUMP_MINOR,
+	};
+
+	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO,
+			   sizeof(args->ptdev->gpu_info));
+	if (IS_ERR(hdr)) {
+		ret = PTR_ERR(hdr);
+		goto free_valloc;
+	}
+
+	gpu_info = alloc_bytes(&alloc, sizeof(*gpu_info));
+	if (IS_ERR(gpu_info)) {
+		ret = PTR_ERR(gpu_info);
+		goto free_valloc;
+	}
+
+	*gpu_info = args->ptdev->gpu_info;
+
+	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO,
+			   sizeof(args->ptdev->csif_info));
+	if (IS_ERR(hdr)) {
+		ret = PTR_ERR(hdr);
+		goto free_valloc;
+	}
+
+	csif_info = alloc_bytes(&alloc, sizeof(*csif_info));
+	if (IS_ERR(csif_info)) {
+		ret = PTR_ERR(csif_info);
+		goto free_valloc;
+	}
+
+	*csif_info = args->ptdev->csif_info;
+
+	hdr = alloc_header(&alloc, DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO,
+			   sizeof(args->ptdev->fw_info));
+	if (IS_ERR(hdr)) {
+		ret = PTR_ERR(hdr);
+		goto free_valloc;
+	}
+
+	fw_info = alloc_bytes(&alloc, sizeof(*fw_info));
+	if (IS_ERR(fw_info)) {
+		ret = PTR_ERR(fw_info);
+		goto free_valloc;
+	}
+
+	*fw_info = args->ptdev->fw_info;
+
+	dump_va_args.ptdev = args->ptdev;
+	dump_va_args.alloc = &alloc;
+	ret = panthor_vm_foreach_va(args->group_vm, dump_va_cb, &dump_va_args);
+	if (ret)
+		goto free_valloc;
+
+	dump_group_args =
+		(struct dump_group_args){ args->ptdev, &alloc, args->group };
+	panthor_sched_get_groupinfo(args->group, &group_info);
+	dump_group_info(&dump_group_args, &group_info);
+
+	if (alloc.pos < dump_size)
+		drm_warn(&args->ptdev->base,
+			 "dump size mismatch: expected %d, got %zu\n",
+			 dump_size, alloc.pos);
+
+	dev_coredumpv(args->ptdev->base.dev, alloc.start, alloc.pos,
+		      GFP_KERNEL);
+
+	return ret;
+
+free_valloc:
+	vfree(mem);
+	return ret;
+}
diff --git a/drivers/gpu/drm/panthor/panthor_dump.h b/drivers/gpu/drm/panthor/panthor_dump.h
new file mode 100644
index 000000000000..2a02943a2dbd
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_dump.h
@@ -0,0 +1,21 @@ 
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* SPDX-FileCopyrightText: Copyright Collabora 2024 */
+
+#ifndef __PANTHOR_DUMP_H__
+#define __PANTHOR_DUMP_H__
+
+#include <drm/drm_gpuvm.h>
+#include <drm/panthor_drm.h>
+
+#include "panthor_device.h"
+#include "panthor_gem.h"
+
+struct panthor_core_dump_args {
+	struct panthor_device *ptdev;
+	struct panthor_vm *group_vm;
+	struct panthor_group *group;
+};
+
+int panthor_core_dump(struct panthor_core_dump_args *args);
+
+#endif /* __PANTHOR_DUMP_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 412e95fcfb92..61d61157ace0 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -2632,6 +2632,28 @@  int panthor_vm_prepare_mapped_bos_resvs(struct drm_exec *exec, struct panthor_vm
 	return drm_gpuvm_prepare_objects(&vm->base, exec, slot_count);
 }
 
+/**
+ * panthor_vm_foreachva() - Execute a callback for each VA in a VM
+ *
+ */
+int panthor_vm_foreach_va(struct panthor_vm *vm,
+			  int (*cb)(void *priv, const struct drm_gpuva *va),
+			  void *priv)
+{
+	struct drm_gpuva *va;
+	int ret = 0;
+
+	mutex_lock(&vm->op_lock);
+	drm_gpuvm_for_each_va(va, &vm->base) {
+		ret = cb(priv, va);
+		if (ret)
+			break;
+	}
+	mutex_unlock(&vm->op_lock);
+
+	return ret;
+}
+
 /**
  * panthor_mmu_unplug() - Unplug the MMU logic
  * @ptdev: Device.
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h
index 6788771071e3..05a5d68b23ae 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.h
+++ b/drivers/gpu/drm/panthor/panthor_mmu.h
@@ -8,6 +8,7 @@ 
 #include <linux/dma-resv.h>
 
 struct drm_exec;
+struct drm_gpuva;
 struct drm_sched_job;
 struct panthor_gem_object;
 struct panthor_heap_pool;
@@ -52,6 +53,11 @@  void panthor_vm_add_job_fence_to_bos_resvs(struct panthor_vm *vm,
 					   struct drm_sched_job *job);
 
 struct dma_resv *panthor_vm_resv(struct panthor_vm *vm);
+
+int panthor_vm_foreach_va(struct panthor_vm *vm,
+			  int (*cb)(void *priv, const struct drm_gpuva *va),
+			  void *priv);
+
 struct drm_gem_object *panthor_vm_root_gem(struct panthor_vm *vm);
 
 void panthor_vm_pool_destroy(struct panthor_file *pfile);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index e0ecc8bcfaae..59c30b311ee9 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -24,6 +24,7 @@ 
 
 #include "panthor_devfreq.h"
 #include "panthor_device.h"
+#include "panthor_dump.h"
 #include "panthor_fw.h"
 #include "panthor_gem.h"
 #include "panthor_gpu.h"
@@ -2805,6 +2806,45 @@  static void group_sync_upd_work(struct work_struct *work)
 	group_put(group);
 }
 
+/**
+ * panthor_sched_get_groupinfo() - Build a group info structure for the group
+ *
+ * @group: the group to build a group info structure for
+ * @info: the group info structure to fill
+ */
+void panthor_sched_get_groupinfo(struct panthor_group *group,
+				 struct drm_panthor_dump_group_info *info)
+{
+	info->queue_count = group->queue_count;
+	info->faulty_bitmask = group->fatal_queues;
+}
+
+/** panthor_sched_get_queueinfo() - Build a queue info structure for a queue
+ * given its group and its cs_id
+ *
+ * @group: the group the queue belongs to
+ * @cs_id: the command stream ID of the queue
+ * @info: the queue info structure to fill
+ */
+int panthor_sched_get_queueinfo(struct panthor_group *group, u32 cs_id,
+				struct drm_panthor_dump_queue_info *info)
+{
+	struct panthor_queue *queue;
+
+	if (cs_id >= group->queue_count)
+		return -EINVAL;
+
+	queue = group->queues[cs_id];
+
+	info->cs_id = cs_id;
+	info->ringbuf_insert = queue->iface.input->insert;
+	info->ringbuf_extract = queue->iface.output->extract;
+	info->ringbuf_gpuva = panthor_kernel_bo_gpuva(queue->ringbuf);
+	info->ringbuf_size = panthor_kernel_bo_size(queue->ringbuf);
+
+	return 0;
+}
+
 static struct dma_fence *
 queue_run_job(struct drm_sched_job *sched_job)
 {
@@ -2946,7 +2986,7 @@  queue_timedout_job(struct drm_sched_job *sched_job)
 	struct panthor_device *ptdev = group->ptdev;
 	struct panthor_scheduler *sched = ptdev->scheduler;
 	struct panthor_queue *queue = group->queues[job->queue_idx];
-
+	struct panthor_core_dump_args core_dump_args;
 	drm_warn(&ptdev->base, "job timeout\n");
 
 	drm_WARN_ON(&ptdev->base, atomic_read(&sched->reset.in_progress));
@@ -2955,6 +2995,15 @@  queue_timedout_job(struct drm_sched_job *sched_job)
 
 	mutex_lock(&sched->lock);
 	group->timedout = true;
+
+	core_dump_args = (struct panthor_core_dump_args) {
+		.ptdev = ptdev,
+		.group_vm = job->group->vm,
+		.group = job->group,
+	};
+
+	panthor_core_dump(&core_dump_args);
+
 	if (group->csg_id >= 0) {
 		sched_queue_delayed_work(ptdev->scheduler, tick, 0);
 	} else {
diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
index 3a30d2328b30..9a5b53498dcc 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.h
+++ b/drivers/gpu/drm/panthor/panthor_sched.h
@@ -17,6 +17,9 @@  struct panthor_device;
 struct panthor_file;
 struct panthor_group_pool;
 struct panthor_job;
+struct panthor_group;
+struct drm_panthor_dump_group_info;
+struct drm_panthor_dump_queue_info;
 
 int panthor_group_create(struct panthor_file *pfile,
 			 const struct drm_panthor_group_create *group_args,
@@ -41,6 +44,13 @@  int panthor_sched_init(struct panthor_device *ptdev);
 void panthor_sched_unplug(struct panthor_device *ptdev);
 void panthor_sched_pre_reset(struct panthor_device *ptdev);
 void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed);
+
+void panthor_sched_get_groupinfo(struct panthor_group *group,
+				 struct drm_panthor_dump_group_info *info);
+
+int panthor_sched_get_queueinfo(struct panthor_group *group, u32 cs_id,
+				struct drm_panthor_dump_queue_info *info);
+
 void panthor_sched_suspend(struct panthor_device *ptdev);
 void panthor_sched_resume(struct panthor_device *ptdev);
 
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index e235cf452460..82ec0f20c49e 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -969,6 +969,130 @@  struct drm_panthor_tiler_heap_destroy {
 	__u32 pad;
 };
 
+/**
+ * enum drm_panthor_dump_header_type - Identifies the type of data that follows
+ * in a panthor core dump.
+ */
+enum drm_panthor_dump_header_type {
+	DRM_PANTHOR_DUMP_HEADER_TYPE_VERSION = 0,
+	/**
+	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO: Gpu information.
+	 */
+	DRM_PANTHOR_DUMP_HEADER_TYPE_GPU_INFO = 1,
+	/**
+	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO: Command stream interface information.
+	 */
+	DRM_PANTHOR_DUMP_HEADER_TYPE_CSIF_INFO = 2,
+	/**
+	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO: Information about the firmware.
+	 */
+	DRM_PANTHOR_DUMP_HEADER_TYPE_FW_INFO = 3,
+	/**
+	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_VM: A dump of the VM for the context.
+	 */
+	DRM_PANTHOR_DUMP_HEADER_TYPE_VM = 4,
+	/**
+	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO: Describes a group. A dump can
+	 * contain either the faulty group, or all groups for the DRM FD.
+	 */
+	DRM_PANTHOR_DUMP_HEADER_TYPE_GROUP_INFO = 5,
+	/**
+	 * @DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO: Describes a faulty queue. This
+	 * will immediately follow a group info.
+	 */
+	DRM_PANTHOR_DUMP_HEADER_TYPE_QUEUE_INFO = 6,
+};
+
+/**
+ * struct drm_panthor_dump_header - A header that describes a section of a panthor core dump.
+ */
+struct drm_panthor_dump_header {
+	/** @magic: Always set to PANT (0x544e4150). */
+	__u32 magic;
+
+	/** @header_type: Identifies the type of data in the following section of the
+	 * core dump file
+	 */
+	enum drm_panthor_dump_header_type header_type;
+
+	/** @header_size: The size of the header.
+	 *
+	 * This is for backward-compatibility purposes in case this structure is
+	 * augmented in the future. It allows userspace to skip over the header and
+	 * access the actual data it describes.
+	 */
+	__u32 header_size;
+
+	/** @data_size: The size of the following section */
+	__u32 data_size;
+};
+
+/**
+ * struct drm_panthor_dump_version - Version information for a Panthor GPU dump.
+ *
+ * This structure is used to hold version information when performing a dump of
+ * the state of a Panthor GPU.
+ */
+struct drm_panthor_dump_version {
+	/** @major: Versioning information for backwards compatibility */
+	__u32 major;
+	/** @minor: Versioning information for backwards compatibility */
+	__u32 minor;
+};
+
+/**
+ * struct drm_panthor_dump_group_info - Group information for a Panthor GPU
+ * dump.
+ *
+ * This structure is used to hold information about a group when performing a
+ * dump of the state of a Panthor GPU.
+ */
+struct drm_panthor_dump_group_info {
+	/** @queue_count: The number of queues in the group. */
+	__u32 queue_count;
+	/** @faulty_queues: A bitmask denoting the faulty queues */
+	__u32 faulty_bitmask;
+};
+
+#define DRM_PANTHOR_DUMP_QUEUE_INFO_FLAGS_FAULTY	(1 << 0)
+
+/**
+ * struct drm_panthor_dump_queue_info - Queue information for a Panthor GPU
+ * dump.
+ *
+ * This structure is used to hold information about a queue when performing a
+ * dump of the state of a Panthor GPU.
+ */
+struct drm_panthor_dump_queue_info {
+	/** See DRM_PANTHOR_DUMP_QUEUE_INFO_FLAGS_XXX */
+	u32 flags;
+	/** @cs_id: The ID of the command stream. */
+	__s32 cs_id;
+	/** @faulty: Whether this queue has faulted */
+	/** @ringbuf_gpuva: The GPU virtual address of the ring buffer. */
+	__u64 ringbuf_gpuva;
+	/** @ringbuf_insert: The insert point (i.e.: offset) in the ring buffer. This
+	 * is where a instruction would be inserted next by the CPU.
+	 */
+	__u64 ringbuf_insert;
+	/** @ringbuf_extract: The extract point (i.e.: offset) in the ring buffer.
+	 * This is where the GPU would read the next instruction.
+	 */
+	__u64 ringbuf_extract;
+	/** @ringbuf_size: The size of the ring buffer */
+	__u64 ringbuf_size;
+};
+
+/**
+ * struct drm_panthor_dump_gpuva - Describes a GPU VA range in the dump.
+ */
+struct drm_panthor_dump_gpuva {
+	/** @addr: The start address for the mapping */
+	__u64 addr;
+	/** @range: The range covered by the VA mapping */
+	__u64 range;
+};
+
 #if defined(__cplusplus)
 }
 #endif