[3/5] drm/lima: save task info dump when task fail
diff mbox series

Message ID 20200222024210.18697-4-yuq825@gmail.com
State New
Headers show
Series
  • drm/lima: add error debug functionality
Related show

Commit Message

Qiang Yu Feb. 22, 2020, 2:42 a.m. UTC
Save all information to start a task which can be exported to user
for debug usage. Dump file data format is specified in lima_dump.h

Signed-off-by: Qiang Yu <yuq825@gmail.com>
---
 drivers/gpu/drm/lima/lima_device.c |  13 +++
 drivers/gpu/drm/lima/lima_device.h |   8 ++
 drivers/gpu/drm/lima/lima_dump.h   |  77 +++++++++++++++++
 drivers/gpu/drm/lima/lima_sched.c  | 128 +++++++++++++++++++++++++++++
 drivers/gpu/drm/lima/lima_sched.h  |   7 ++
 5 files changed, 233 insertions(+)
 create mode 100644 drivers/gpu/drm/lima/lima_dump.h

Comments

kbuild test robot Feb. 23, 2020, 2:07 a.m. UTC | #1
Hi Qiang,

I love your patch! Yet something to improve:

[auto build test ERROR on drm-tip/drm-tip]
[cannot apply to drm-exynos/exynos-drm-next drm-intel/for-linux-next tegra-drm/drm/tegra/for-next linus/master v5.6-rc2 next-20200221]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Qiang-Yu/drm-lima-add-error-debug-functionality/20200223-054634
base:   git://anongit.freedesktop.org/drm/drm-tip drm-tip
config: mips-allyesconfig (attached as .config)
compiler: mips-linux-gcc (GCC) 7.5.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.5.0 make.cross ARCH=mips 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/gpu/drm/lima/lima_sched.c: In function 'lima_sched_build_error_task_list':
>> drivers/gpu/drm/lima/lima_sched.c:347:11: error: implicit declaration of function 'vmap'; did you mean 'bmap'? [-Werror=implicit-function-declaration]
       data = vmap(bo->base.pages, bo->heap_size >> PAGE_SHIFT,
              ^~~~
              bmap
>> drivers/gpu/drm/lima/lima_sched.c:348:9: error: 'VM_MAP' undeclared (first use in this function); did you mean 'VM_MPX'?
            VM_MAP, pgprot_writecombine(PAGE_KERNEL));
            ^~~~~~
            VM_MPX
   drivers/gpu/drm/lima/lima_sched.c:348:9: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/gpu/drm/lima/lima_sched.c:356:4: error: implicit declaration of function 'vunmap'; did you mean 'kunmap'? [-Werror=implicit-function-declaration]
       vunmap(data);
       ^~~~~~
       kunmap
   cc1: some warnings being treated as errors

vim +347 drivers/gpu/drm/lima/lima_sched.c

   258	
   259	static void lima_sched_build_error_task_list(struct lima_sched_task *task)
   260	{
   261		struct lima_sched_error_task *et;
   262		struct lima_sched_pipe *pipe = to_lima_pipe(task->base.sched);
   263		struct lima_ip *ip = pipe->processor[0];
   264		int pipe_id = ip->id == lima_ip_gp ? lima_pipe_gp : lima_pipe_pp;
   265		struct lima_device *dev = ip->dev;
   266		struct lima_sched_context *sched_ctx =
   267			container_of(task->base.entity, struct lima_sched_context, base);
   268		struct lima_ctx *ctx =
   269			container_of(sched_ctx, struct lima_ctx, context[pipe_id]);
   270		struct lima_dump_task *dt;
   271		struct lima_dump_chunk *chunk;
   272		struct lima_dump_chunk_pid *pid_chunk;
   273		struct lima_dump_chunk_buffer *buffer_chunk;
   274		uint32_t size, task_size, mem_size;
   275		int i;
   276	
   277		mutex_lock(&dev->error_task_list_lock);
   278	
   279		if (dev->dump.num_tasks >= lima_max_error_tasks) {
   280			dev_info(dev->dev, "fail to save task state: error task list is full\n");
   281			goto out;
   282		}
   283	
   284		/* frame chunk */
   285		size = sizeof(struct lima_dump_chunk) + pipe->frame_size;
   286		/* process name chunk */
   287		size += sizeof(struct lima_dump_chunk) + sizeof(ctx->pname);
   288		/* pid chunk */
   289		size += sizeof(struct lima_dump_chunk);
   290		/* buffer chunks */
   291		for (i = 0; i < task->num_bos; i++) {
   292			struct lima_bo *bo = task->bos[i];
   293	
   294			size += sizeof(struct lima_dump_chunk);
   295			size += bo->heap_size ? bo->heap_size : lima_bo_size(bo);
   296		}
   297	
   298		task_size = size + sizeof(struct lima_dump_task);
   299		mem_size = task_size + sizeof(*et);
   300		et = kvmalloc(mem_size, GFP_KERNEL);
   301		if (!et) {
   302			dev_err(dev->dev, "fail to alloc task dump buffer of size %x\n",
   303				mem_size);
   304			goto out;
   305		}
   306	
   307		et->data = et + 1;
   308		et->size = task_size;
   309	
   310		dt = et->data;
   311		memset(dt, 0, sizeof(*dt));
   312		dt->id = pipe_id;
   313		dt->size = size;
   314	
   315		chunk = (struct lima_dump_chunk *)(dt + 1);
   316		memset(chunk, 0, sizeof(*chunk));
   317		chunk->id = LIMA_DUMP_CHUNK_FRAME;
   318		chunk->size = pipe->frame_size;
   319		memcpy(chunk + 1, task->frame, pipe->frame_size);
   320		dt->num_chunks++;
   321	
   322		chunk = (void *)(chunk + 1) + chunk->size;
   323		memset(chunk, 0, sizeof(*chunk));
   324		chunk->id = LIMA_DUMP_CHUNK_PROCESS_NAME;
   325		chunk->size = sizeof(ctx->pname);
   326		memcpy(chunk + 1, ctx->pname, sizeof(ctx->pname));
   327		dt->num_chunks++;
   328	
   329		pid_chunk = (void *)(chunk + 1) + chunk->size;
   330		memset(pid_chunk, 0, sizeof(*pid_chunk));
   331		pid_chunk->id = LIMA_DUMP_CHUNK_PROCESS_ID;
   332		pid_chunk->pid = ctx->pid;
   333		dt->num_chunks++;
   334	
   335		buffer_chunk = (void *)(pid_chunk + 1) + pid_chunk->size;
   336		for (i = 0; i < task->num_bos; i++) {
   337			struct lima_bo *bo = task->bos[i];
   338			void *data;
   339	
   340			memset(buffer_chunk, 0, sizeof(*buffer_chunk));
   341		        buffer_chunk->id = LIMA_DUMP_CHUNK_BUFFER;
   342			buffer_chunk->va = lima_vm_get_va(task->vm, bo);
   343	
   344			if (bo->heap_size) {
   345				buffer_chunk->size = bo->heap_size;
   346	
 > 347				data = vmap(bo->base.pages, bo->heap_size >> PAGE_SHIFT,
 > 348					    VM_MAP, pgprot_writecombine(PAGE_KERNEL));
   349				if (!data) {
   350					kvfree(et);
   351					goto out;
   352				}
   353	
   354				memcpy(buffer_chunk + 1, data, buffer_chunk->size);
   355	
 > 356				vunmap(data);
   357			} else {
   358				buffer_chunk->size = lima_bo_size(bo);
   359	
   360				data = drm_gem_shmem_vmap(&bo->base.base);
   361				if (IS_ERR_OR_NULL(data)) {
   362					kvfree(et);
   363					goto out;
   364				}
   365	
   366				memcpy(buffer_chunk + 1, data, buffer_chunk->size);
   367	
   368				drm_gem_shmem_vunmap(&bo->base.base, data);
   369			}
   370	
   371			buffer_chunk = (void *)(buffer_chunk + 1) + buffer_chunk->size;
   372			dt->num_chunks++;
   373		}
   374	
   375		list_add(&et->list, &dev->error_task_list);
   376		dev->dump.size += et->size;
   377		dev->dump.num_tasks++;
   378	
   379		dev_info(dev->dev, "save error task state success\n");
   380	
   381	out:
   382		mutex_unlock(&dev->error_task_list_lock);
   383	}
   384	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Vasily Khoruzhick March 3, 2020, 9:06 a.m. UTC | #2
On Fri, Feb 21, 2020 at 6:43 PM Qiang Yu <yuq825@gmail.com> wrote:
>
> Save all information to start a task which can be exported to user
> for debug usage. Dump file data format is specified in lima_dump.h

Looks like lima_sched.c is missing #include <linux/vmalloc.h> so
kbuild bot complains

> Signed-off-by: Qiang Yu <yuq825@gmail.com>
> ---
>  drivers/gpu/drm/lima/lima_device.c |  13 +++
>  drivers/gpu/drm/lima/lima_device.h |   8 ++
>  drivers/gpu/drm/lima/lima_dump.h   |  77 +++++++++++++++++
>  drivers/gpu/drm/lima/lima_sched.c  | 128 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/lima/lima_sched.h  |   7 ++
>  5 files changed, 233 insertions(+)
>  create mode 100644 drivers/gpu/drm/lima/lima_dump.h
>
> diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c
> index 19829b543024..42a00171fea5 100644
> --- a/drivers/gpu/drm/lima/lima_device.c
> +++ b/drivers/gpu/drm/lima/lima_device.c
> @@ -344,6 +344,12 @@ int lima_device_init(struct lima_device *ldev)
>         if (err)
>                 goto err_out5;
>
> +       ldev->dump.magic = LIMA_DUMP_MAGIC;
> +       ldev->dump.version_major = LIMA_DUMP_MAJOR;
> +       ldev->dump.version_minor = LIMA_DUMP_MINOR;
> +       INIT_LIST_HEAD(&ldev->error_task_list);
> +       mutex_init(&ldev->error_task_list_lock);
> +
>         dev_info(ldev->dev, "bus rate = %lu\n", clk_get_rate(ldev->clk_bus));
>         dev_info(ldev->dev, "mod rate = %lu", clk_get_rate(ldev->clk_gpu));
>
> @@ -370,6 +376,13 @@ int lima_device_init(struct lima_device *ldev)
>  void lima_device_fini(struct lima_device *ldev)
>  {
>         int i;
> +       struct lima_sched_error_task *et, *tmp;
> +
> +       list_for_each_entry_safe(et, tmp, &ldev->error_task_list, list) {
> +               list_del(&et->list);
> +               kvfree(et);
> +       }
> +       mutex_destroy(&ldev->error_task_list_lock);
>
>         lima_fini_pp_pipe(ldev);
>         lima_fini_gp_pipe(ldev);
> diff --git a/drivers/gpu/drm/lima/lima_device.h b/drivers/gpu/drm/lima/lima_device.h
> index 31158d86271c..f17173f47f26 100644
> --- a/drivers/gpu/drm/lima/lima_device.h
> +++ b/drivers/gpu/drm/lima/lima_device.h
> @@ -6,8 +6,11 @@
>
>  #include <drm/drm_device.h>
>  #include <linux/delay.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
>
>  #include "lima_sched.h"
> +#include "lima_dump.h"
>
>  enum lima_gpu_id {
>         lima_gpu_mali400 = 0,
> @@ -94,6 +97,11 @@ struct lima_device {
>
>         u32 *dlbu_cpu;
>         dma_addr_t dlbu_dma;
> +
> +       /* debug info */
> +       struct lima_dump_head dump;
> +       struct list_head error_task_list;
> +       struct mutex error_task_list_lock;
>  };
>
>  static inline struct lima_device *
> diff --git a/drivers/gpu/drm/lima/lima_dump.h b/drivers/gpu/drm/lima/lima_dump.h
> new file mode 100644
> index 000000000000..ca243d99c51b
> --- /dev/null
> +++ b/drivers/gpu/drm/lima/lima_dump.h
> @@ -0,0 +1,77 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> +/* Copyright 2020 Qiang Yu <yuq825@gmail.com> */
> +
> +#ifndef __LIMA_DUMP_H__
> +#define __LIMA_DUMP_H__
> +
> +#include <linux/types.h>
> +
> +/**
> + * dump file format for all the information to start a lima task
> + *
> + * top level format
> + * | magic code "LIMA" | format version | num tasks | data size |
> + * | reserved | reserved | reserved | reserved |
> + * | task 1 ID | task 1 size | num chunks | reserved | task 1 data |
> + * | task 2 ID | task 2 size | num chunks | reserved | task 2 data |
> + * ...
> + *
> + * task data format
> + * | chunk 1 ID | chunk 1 size | reserved | reserved | chunk 1 data |
> + * | chunk 2 ID | chunk 2 size | reserved | reserved | chunk 2 data |
> + * ...
> + *
> + */
> +
> +#define LIMA_DUMP_MAJOR 1
> +#define LIMA_DUMP_MINOR 0
> +
> +#define LIMA_DUMP_MAGIC 0x414d494c
> +
> +struct lima_dump_head {
> +       __u32 magic;
> +       __u16 version_major;
> +       __u16 version_minor;
> +       __u32 num_tasks;
> +       __u32 size;
> +       __u32 reserved[4];
> +};
> +
> +#define LIMA_DUMP_TASK_GP   0
> +#define LIMA_DUMP_TASK_PP   1
> +#define LIMA_DUMP_TASK_NUM  2
> +
> +struct lima_dump_task {
> +       __u32 id;
> +       __u32 size;
> +       __u32 num_chunks;
> +       __u32 reserved;
> +};
> +
> +#define LIMA_DUMP_CHUNK_FRAME         0
> +#define LIMA_DUMP_CHUNK_BUFFER        1
> +#define LIMA_DUMP_CHUNK_PROCESS_NAME  2
> +#define LIMA_DUMP_CHUNK_PROCESS_ID    3
> +#define LIMA_DUMP_CHUNK_NUM           4
> +
> +struct lima_dump_chunk {
> +       __u32 id;
> +       __u32 size;
> +       __u32 reserved[2];
> +};
> +
> +struct lima_dump_chunk_buffer {
> +       __u32 id;
> +       __u32 size;
> +       __u32 va;
> +       __u32 reserved;
> +};
> +
> +struct lima_dump_chunk_pid {
> +       __u32 id;
> +       __u32 size;
> +       __u32 pid;
> +       __u32 reserved;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c
> index 3886999b4533..cd1bf3ad9bb5 100644
> --- a/drivers/gpu/drm/lima/lima_sched.c
> +++ b/drivers/gpu/drm/lima/lima_sched.c
> @@ -256,6 +256,132 @@ static struct dma_fence *lima_sched_run_job(struct drm_sched_job *job)
>         return task->fence;
>  }
>
> +static void lima_sched_build_error_task_list(struct lima_sched_task *task)
> +{
> +       struct lima_sched_error_task *et;
> +       struct lima_sched_pipe *pipe = to_lima_pipe(task->base.sched);
> +       struct lima_ip *ip = pipe->processor[0];
> +       int pipe_id = ip->id == lima_ip_gp ? lima_pipe_gp : lima_pipe_pp;
> +       struct lima_device *dev = ip->dev;
> +       struct lima_sched_context *sched_ctx =
> +               container_of(task->base.entity, struct lima_sched_context, base);
> +       struct lima_ctx *ctx =
> +               container_of(sched_ctx, struct lima_ctx, context[pipe_id]);
> +       struct lima_dump_task *dt;
> +       struct lima_dump_chunk *chunk;
> +       struct lima_dump_chunk_pid *pid_chunk;
> +       struct lima_dump_chunk_buffer *buffer_chunk;
> +       uint32_t size, task_size, mem_size;
> +       int i;
> +
> +       mutex_lock(&dev->error_task_list_lock);
> +
> +       if (dev->dump.num_tasks >= lima_max_error_tasks) {
> +               dev_info(dev->dev, "fail to save task state: error task list is full\n");
> +               goto out;
> +       }
> +
> +       /* frame chunk */
> +       size = sizeof(struct lima_dump_chunk) + pipe->frame_size;
> +       /* process name chunk */
> +       size += sizeof(struct lima_dump_chunk) + sizeof(ctx->pname);
> +       /* pid chunk */
> +       size += sizeof(struct lima_dump_chunk);
> +       /* buffer chunks */
> +       for (i = 0; i < task->num_bos; i++) {
> +               struct lima_bo *bo = task->bos[i];
> +
> +               size += sizeof(struct lima_dump_chunk);
> +               size += bo->heap_size ? bo->heap_size : lima_bo_size(bo);
> +       }
> +
> +       task_size = size + sizeof(struct lima_dump_task);
> +       mem_size = task_size + sizeof(*et);
> +       et = kvmalloc(mem_size, GFP_KERNEL);
> +       if (!et) {
> +               dev_err(dev->dev, "fail to alloc task dump buffer of size %x\n",
> +                       mem_size);
> +               goto out;
> +       }
> +
> +       et->data = et + 1;
> +       et->size = task_size;
> +
> +       dt = et->data;
> +       memset(dt, 0, sizeof(*dt));
> +       dt->id = pipe_id;
> +       dt->size = size;
> +
> +       chunk = (struct lima_dump_chunk *)(dt + 1);
> +       memset(chunk, 0, sizeof(*chunk));
> +       chunk->id = LIMA_DUMP_CHUNK_FRAME;
> +       chunk->size = pipe->frame_size;
> +       memcpy(chunk + 1, task->frame, pipe->frame_size);
> +       dt->num_chunks++;
> +
> +       chunk = (void *)(chunk + 1) + chunk->size;
> +       memset(chunk, 0, sizeof(*chunk));
> +       chunk->id = LIMA_DUMP_CHUNK_PROCESS_NAME;
> +       chunk->size = sizeof(ctx->pname);
> +       memcpy(chunk + 1, ctx->pname, sizeof(ctx->pname));
> +       dt->num_chunks++;
> +
> +       pid_chunk = (void *)(chunk + 1) + chunk->size;
> +       memset(pid_chunk, 0, sizeof(*pid_chunk));
> +       pid_chunk->id = LIMA_DUMP_CHUNK_PROCESS_ID;
> +       pid_chunk->pid = ctx->pid;
> +       dt->num_chunks++;
> +
> +       buffer_chunk = (void *)(pid_chunk + 1) + pid_chunk->size;
> +       for (i = 0; i < task->num_bos; i++) {
> +               struct lima_bo *bo = task->bos[i];
> +               void *data;
> +
> +               memset(buffer_chunk, 0, sizeof(*buffer_chunk));
> +               buffer_chunk->id = LIMA_DUMP_CHUNK_BUFFER;
> +               buffer_chunk->va = lima_vm_get_va(task->vm, bo);
> +
> +               if (bo->heap_size) {
> +                       buffer_chunk->size = bo->heap_size;
> +
> +                       data = vmap(bo->base.pages, bo->heap_size >> PAGE_SHIFT,
> +                                   VM_MAP, pgprot_writecombine(PAGE_KERNEL));
> +                       if (!data) {
> +                               kvfree(et);
> +                               goto out;
> +                       }
> +
> +                       memcpy(buffer_chunk + 1, data, buffer_chunk->size);
> +
> +                       vunmap(data);
> +               } else {
> +                       buffer_chunk->size = lima_bo_size(bo);
> +
> +                       data = drm_gem_shmem_vmap(&bo->base.base);
> +                       if (IS_ERR_OR_NULL(data)) {
> +                               kvfree(et);
> +                               goto out;
> +                       }
> +
> +                       memcpy(buffer_chunk + 1, data, buffer_chunk->size);
> +
> +                       drm_gem_shmem_vunmap(&bo->base.base, data);
> +               }
> +
> +               buffer_chunk = (void *)(buffer_chunk + 1) + buffer_chunk->size;
> +               dt->num_chunks++;
> +       }
> +
> +       list_add(&et->list, &dev->error_task_list);
> +       dev->dump.size += et->size;
> +       dev->dump.num_tasks++;
> +
> +       dev_info(dev->dev, "save error task state success\n");
> +
> +out:
> +       mutex_unlock(&dev->error_task_list_lock);
> +}
> +
>  static void lima_sched_timedout_job(struct drm_sched_job *job)
>  {
>         struct lima_sched_pipe *pipe = to_lima_pipe(job->sched);
> @@ -268,6 +394,8 @@ static void lima_sched_timedout_job(struct drm_sched_job *job)
>
>         drm_sched_increase_karma(&task->base);
>
> +       lima_sched_build_error_task_list(task);
> +
>         pipe->task_error(pipe);
>
>         if (pipe->bcast_mmu)
> diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h
> index d64393fb50a9..e29f5e3b675b 100644
> --- a/drivers/gpu/drm/lima/lima_sched.h
> +++ b/drivers/gpu/drm/lima/lima_sched.h
> @@ -5,9 +5,16 @@
>  #define __LIMA_SCHED_H__
>
>  #include <drm/gpu_scheduler.h>
> +#include <linux/list.h>
>
>  struct lima_vm;
>
> +struct lima_sched_error_task {
> +       struct list_head list;
> +       void *data;
> +       uint32_t size;
> +};
> +
>  struct lima_sched_task {
>         struct drm_sched_job base;
>
> --
> 2.17.1
>

Patch
diff mbox series

diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c
index 19829b543024..42a00171fea5 100644
--- a/drivers/gpu/drm/lima/lima_device.c
+++ b/drivers/gpu/drm/lima/lima_device.c
@@ -344,6 +344,12 @@  int lima_device_init(struct lima_device *ldev)
 	if (err)
 		goto err_out5;
 
+	ldev->dump.magic = LIMA_DUMP_MAGIC;
+	ldev->dump.version_major = LIMA_DUMP_MAJOR;
+	ldev->dump.version_minor = LIMA_DUMP_MINOR;
+	INIT_LIST_HEAD(&ldev->error_task_list);
+	mutex_init(&ldev->error_task_list_lock);
+
 	dev_info(ldev->dev, "bus rate = %lu\n", clk_get_rate(ldev->clk_bus));
 	dev_info(ldev->dev, "mod rate = %lu", clk_get_rate(ldev->clk_gpu));
 
@@ -370,6 +376,13 @@  int lima_device_init(struct lima_device *ldev)
 void lima_device_fini(struct lima_device *ldev)
 {
 	int i;
+	struct lima_sched_error_task *et, *tmp;
+
+	list_for_each_entry_safe(et, tmp, &ldev->error_task_list, list) {
+		list_del(&et->list);
+		kvfree(et);
+	}
+	mutex_destroy(&ldev->error_task_list_lock);
 
 	lima_fini_pp_pipe(ldev);
 	lima_fini_gp_pipe(ldev);
diff --git a/drivers/gpu/drm/lima/lima_device.h b/drivers/gpu/drm/lima/lima_device.h
index 31158d86271c..f17173f47f26 100644
--- a/drivers/gpu/drm/lima/lima_device.h
+++ b/drivers/gpu/drm/lima/lima_device.h
@@ -6,8 +6,11 @@ 
 
 #include <drm/drm_device.h>
 #include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
 
 #include "lima_sched.h"
+#include "lima_dump.h"
 
 enum lima_gpu_id {
 	lima_gpu_mali400 = 0,
@@ -94,6 +97,11 @@  struct lima_device {
 
 	u32 *dlbu_cpu;
 	dma_addr_t dlbu_dma;
+
+	/* debug info */
+	struct lima_dump_head dump;
+	struct list_head error_task_list;
+	struct mutex error_task_list_lock;
 };
 
 static inline struct lima_device *
diff --git a/drivers/gpu/drm/lima/lima_dump.h b/drivers/gpu/drm/lima/lima_dump.h
new file mode 100644
index 000000000000..ca243d99c51b
--- /dev/null
+++ b/drivers/gpu/drm/lima/lima_dump.h
@@ -0,0 +1,77 @@ 
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/* Copyright 2020 Qiang Yu <yuq825@gmail.com> */
+
+#ifndef __LIMA_DUMP_H__
+#define __LIMA_DUMP_H__
+
+#include <linux/types.h>
+
+/**
+ * dump file format for all the information to start a lima task
+ *
+ * top level format
+ * | magic code "LIMA" | format version | num tasks | data size |
+ * | reserved | reserved | reserved | reserved |
+ * | task 1 ID | task 1 size | num chunks | reserved | task 1 data |
+ * | task 2 ID | task 2 size | num chunks | reserved | task 2 data |
+ * ...
+ *
+ * task data format
+ * | chunk 1 ID | chunk 1 size | reserved | reserved | chunk 1 data |
+ * | chunk 2 ID | chunk 2 size | reserved | reserved | chunk 2 data |
+ * ...
+ *
+ */
+
+#define LIMA_DUMP_MAJOR 1
+#define LIMA_DUMP_MINOR 0
+
+#define LIMA_DUMP_MAGIC 0x414d494c
+
+struct lima_dump_head {
+	__u32 magic;
+	__u16 version_major;
+	__u16 version_minor;
+	__u32 num_tasks;
+	__u32 size;
+	__u32 reserved[4];
+};
+
+#define LIMA_DUMP_TASK_GP   0
+#define LIMA_DUMP_TASK_PP   1
+#define LIMA_DUMP_TASK_NUM  2
+
+struct lima_dump_task {
+	__u32 id;
+	__u32 size;
+	__u32 num_chunks;
+	__u32 reserved;
+};
+
+#define LIMA_DUMP_CHUNK_FRAME         0
+#define LIMA_DUMP_CHUNK_BUFFER        1
+#define LIMA_DUMP_CHUNK_PROCESS_NAME  2
+#define LIMA_DUMP_CHUNK_PROCESS_ID    3
+#define LIMA_DUMP_CHUNK_NUM           4
+
+struct lima_dump_chunk {
+	__u32 id;
+	__u32 size;
+	__u32 reserved[2];
+};
+
+struct lima_dump_chunk_buffer {
+	__u32 id;
+	__u32 size;
+	__u32 va;
+	__u32 reserved;
+};
+
+struct lima_dump_chunk_pid {
+	__u32 id;
+	__u32 size;
+	__u32 pid;
+	__u32 reserved;
+};
+
+#endif
diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c
index 3886999b4533..cd1bf3ad9bb5 100644
--- a/drivers/gpu/drm/lima/lima_sched.c
+++ b/drivers/gpu/drm/lima/lima_sched.c
@@ -256,6 +256,132 @@  static struct dma_fence *lima_sched_run_job(struct drm_sched_job *job)
 	return task->fence;
 }
 
+static void lima_sched_build_error_task_list(struct lima_sched_task *task)
+{
+	struct lima_sched_error_task *et;
+	struct lima_sched_pipe *pipe = to_lima_pipe(task->base.sched);
+	struct lima_ip *ip = pipe->processor[0];
+	int pipe_id = ip->id == lima_ip_gp ? lima_pipe_gp : lima_pipe_pp;
+	struct lima_device *dev = ip->dev;
+	struct lima_sched_context *sched_ctx =
+		container_of(task->base.entity, struct lima_sched_context, base);
+	struct lima_ctx *ctx =
+		container_of(sched_ctx, struct lima_ctx, context[pipe_id]);
+	struct lima_dump_task *dt;
+	struct lima_dump_chunk *chunk;
+	struct lima_dump_chunk_pid *pid_chunk;
+	struct lima_dump_chunk_buffer *buffer_chunk;
+	uint32_t size, task_size, mem_size;
+	int i;
+
+	mutex_lock(&dev->error_task_list_lock);
+
+	if (dev->dump.num_tasks >= lima_max_error_tasks) {
+		dev_info(dev->dev, "fail to save task state: error task list is full\n");
+		goto out;
+	}
+
+	/* frame chunk */
+	size = sizeof(struct lima_dump_chunk) + pipe->frame_size;
+	/* process name chunk */
+	size += sizeof(struct lima_dump_chunk) + sizeof(ctx->pname);
+	/* pid chunk */
+	size += sizeof(struct lima_dump_chunk);
+	/* buffer chunks */
+	for (i = 0; i < task->num_bos; i++) {
+		struct lima_bo *bo = task->bos[i];
+
+		size += sizeof(struct lima_dump_chunk);
+		size += bo->heap_size ? bo->heap_size : lima_bo_size(bo);
+	}
+
+	task_size = size + sizeof(struct lima_dump_task);
+	mem_size = task_size + sizeof(*et);
+	et = kvmalloc(mem_size, GFP_KERNEL);
+	if (!et) {
+		dev_err(dev->dev, "fail to alloc task dump buffer of size %x\n",
+			mem_size);
+		goto out;
+	}
+
+	et->data = et + 1;
+	et->size = task_size;
+
+	dt = et->data;
+	memset(dt, 0, sizeof(*dt));
+	dt->id = pipe_id;
+	dt->size = size;
+
+	chunk = (struct lima_dump_chunk *)(dt + 1);
+	memset(chunk, 0, sizeof(*chunk));
+	chunk->id = LIMA_DUMP_CHUNK_FRAME;
+	chunk->size = pipe->frame_size;
+	memcpy(chunk + 1, task->frame, pipe->frame_size);
+	dt->num_chunks++;
+
+	chunk = (void *)(chunk + 1) + chunk->size;
+	memset(chunk, 0, sizeof(*chunk));
+	chunk->id = LIMA_DUMP_CHUNK_PROCESS_NAME;
+	chunk->size = sizeof(ctx->pname);
+	memcpy(chunk + 1, ctx->pname, sizeof(ctx->pname));
+	dt->num_chunks++;
+
+	pid_chunk = (void *)(chunk + 1) + chunk->size;
+	memset(pid_chunk, 0, sizeof(*pid_chunk));
+	pid_chunk->id = LIMA_DUMP_CHUNK_PROCESS_ID;
+	pid_chunk->pid = ctx->pid;
+	dt->num_chunks++;
+
+	buffer_chunk = (void *)(pid_chunk + 1) + pid_chunk->size;
+	for (i = 0; i < task->num_bos; i++) {
+		struct lima_bo *bo = task->bos[i];
+		void *data;
+
+		memset(buffer_chunk, 0, sizeof(*buffer_chunk));
+	        buffer_chunk->id = LIMA_DUMP_CHUNK_BUFFER;
+		buffer_chunk->va = lima_vm_get_va(task->vm, bo);
+
+		if (bo->heap_size) {
+			buffer_chunk->size = bo->heap_size;
+
+			data = vmap(bo->base.pages, bo->heap_size >> PAGE_SHIFT,
+				    VM_MAP, pgprot_writecombine(PAGE_KERNEL));
+			if (!data) {
+				kvfree(et);
+				goto out;
+			}
+
+			memcpy(buffer_chunk + 1, data, buffer_chunk->size);
+
+			vunmap(data);
+		} else {
+			buffer_chunk->size = lima_bo_size(bo);
+
+			data = drm_gem_shmem_vmap(&bo->base.base);
+			if (IS_ERR_OR_NULL(data)) {
+				kvfree(et);
+				goto out;
+			}
+
+			memcpy(buffer_chunk + 1, data, buffer_chunk->size);
+
+			drm_gem_shmem_vunmap(&bo->base.base, data);
+		}
+
+		buffer_chunk = (void *)(buffer_chunk + 1) + buffer_chunk->size;
+		dt->num_chunks++;
+	}
+
+	list_add(&et->list, &dev->error_task_list);
+	dev->dump.size += et->size;
+	dev->dump.num_tasks++;
+
+	dev_info(dev->dev, "save error task state success\n");
+
+out:
+	mutex_unlock(&dev->error_task_list_lock);
+}
+
 static void lima_sched_timedout_job(struct drm_sched_job *job)
 {
 	struct lima_sched_pipe *pipe = to_lima_pipe(job->sched);
@@ -268,6 +394,8 @@  static void lima_sched_timedout_job(struct drm_sched_job *job)
 
 	drm_sched_increase_karma(&task->base);
 
+	lima_sched_build_error_task_list(task);
+
 	pipe->task_error(pipe);
 
 	if (pipe->bcast_mmu)
diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h
index d64393fb50a9..e29f5e3b675b 100644
--- a/drivers/gpu/drm/lima/lima_sched.h
+++ b/drivers/gpu/drm/lima/lima_sched.h
@@ -5,9 +5,16 @@ 
 #define __LIMA_SCHED_H__
 
 #include <drm/gpu_scheduler.h>
+#include <linux/list.h>
 
 struct lima_vm;
 
+struct lima_sched_error_task {
+	struct list_head list;
+	void *data;
+	uint32_t size;
+};
+
 struct lima_sched_task {
 	struct drm_sched_job base;