From 2081e8dcf1ee7170c67c0891da5487ac7091d2df Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 16 Jan 2020 21:11:56 +0800 Subject: [PATCH] drm/lima: recover task by enlarging heap buffer Increase heap buffer backup memory when GP receive PLBU out of memory interrupt, then resume the task. Reviewed-by: Vasily Khoruzhick Tested-by: Andreas Baierl Signed-off-by: Qiang Yu Link: https://patchwork.freedesktop.org/patch/msgid/20200116131157.13346-5-yuq825@gmail.com --- drivers/gpu/drm/lima/lima_gp.c | 58 +++++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/lima/lima_mmu.c | 5 ++++ drivers/gpu/drm/lima/lima_mmu.h | 1 + drivers/gpu/drm/lima/lima_sched.c | 35 +++++++++++++++++++---- drivers/gpu/drm/lima/lima_sched.h | 6 ++++ 5 files changed, 98 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/lima/lima_gp.c b/drivers/gpu/drm/lima/lima_gp.c index ccf49faedebf..52b210f9a605 100644 --- a/drivers/gpu/drm/lima/lima_gp.c +++ b/drivers/gpu/drm/lima/lima_gp.c @@ -11,6 +11,8 @@ #include "lima_device.h" #include "lima_gp.h" #include "lima_regs.h" +#include "lima_gem.h" +#include "lima_vm.h" #define gp_write(reg, data) writel(data, ip->iomem + reg) #define gp_read(reg) readl(ip->iomem + reg) @@ -20,6 +22,7 @@ static irqreturn_t lima_gp_irq_handler(int irq, void *data) struct lima_ip *ip = data; struct lima_device *dev = ip->dev; struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp; + struct lima_sched_task *task = pipe->current_task; u32 state = gp_read(LIMA_GP_INT_STAT); u32 status = gp_read(LIMA_GP_STATUS); bool done = false; @@ -29,8 +32,16 @@ static irqreturn_t lima_gp_irq_handler(int irq, void *data) return IRQ_NONE; if (state & LIMA_GP_IRQ_MASK_ERROR) { - dev_err(dev->dev, "gp error irq state=%x status=%x\n", - state, status); + if ((state & LIMA_GP_IRQ_MASK_ERROR) == + LIMA_GP_IRQ_PLBU_OUT_OF_MEM) { + dev_dbg(dev->dev, "gp out of heap irq status=%x\n", + status); + } else { + dev_err(dev->dev, "gp error irq state=%x status=%x\n", + state, status); + if (task) + task->recoverable = false; + } /* mask all interrupts before hard reset */ gp_write(LIMA_GP_INT_MASK, 0); @@ -43,6 +54,7 @@ static irqreturn_t lima_gp_irq_handler(int irq, void *data) bool active = status & (LIMA_GP_STATUS_VS_ACTIVE | LIMA_GP_STATUS_PLBU_ACTIVE); done = valid && !active; + pipe->error = false; } gp_write(LIMA_GP_INT_CLEAR, state); @@ -121,6 +133,22 @@ static void lima_gp_task_run(struct lima_sched_pipe *pipe, u32 cmd = 0; int i; + /* update real heap buffer size for GP */ + for (i = 0; i < task->num_bos; i++) { + struct lima_bo *bo = task->bos[i]; + + if (bo->heap_size && + lima_vm_get_va(task->vm, bo) == + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2]) { + f[LIMA_GP_PLBU_ALLOC_END_ADDR >> 2] = + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2] + + bo->heap_size; + task->recoverable = true; + task->heap = bo; + break; + } + } + if (f[LIMA_GP_VSCL_START_ADDR >> 2] != f[LIMA_GP_VSCL_END_ADDR >> 2]) cmd |= LIMA_GP_CMD_START_VS; @@ -184,6 +212,31 @@ static void lima_gp_task_mmu_error(struct lima_sched_pipe *pipe) lima_sched_pipe_task_done(pipe); } +static int lima_gp_task_recover(struct lima_sched_pipe *pipe) +{ + struct lima_ip *ip = pipe->processor[0]; + struct lima_sched_task *task = pipe->current_task; + struct drm_lima_gp_frame *frame = task->frame; + u32 *f = frame->frame; + size_t fail_size = + f[LIMA_GP_PLBU_ALLOC_END_ADDR >> 2] - + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2]; + + if (fail_size == task->heap->heap_size) { + int ret; + + ret = lima_heap_alloc(task->heap, task->vm); + if (ret < 0) + return ret; + } + + gp_write(LIMA_GP_INT_MASK, LIMA_GP_IRQ_MASK_USED); + gp_write(LIMA_GP_PLBU_ALLOC_END_ADDR, + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2] + task->heap->heap_size); + gp_write(LIMA_GP_CMD, LIMA_GP_CMD_UPDATE_PLBU_ALLOC); + return 0; +} + static void lima_gp_print_version(struct lima_ip *ip) { u32 version, major, minor; @@ -270,6 +323,7 @@ int lima_gp_pipe_init(struct lima_device *dev) pipe->task_fini = lima_gp_task_fini; pipe->task_error = lima_gp_task_error; pipe->task_mmu_error = lima_gp_task_mmu_error; + pipe->task_recover = lima_gp_task_recover; return 0; } diff --git a/drivers/gpu/drm/lima/lima_mmu.c b/drivers/gpu/drm/lima/lima_mmu.c index 97ec09dee572..f79d2af427e7 100644 --- a/drivers/gpu/drm/lima/lima_mmu.c +++ b/drivers/gpu/drm/lima/lima_mmu.c @@ -99,6 +99,11 @@ void lima_mmu_fini(struct lima_ip *ip) } +void lima_mmu_flush_tlb(struct lima_ip *ip) +{ + mmu_write(LIMA_MMU_COMMAND, LIMA_MMU_COMMAND_ZAP_CACHE); +} + void lima_mmu_switch_vm(struct lima_ip *ip, struct lima_vm *vm) { struct lima_device *dev = ip->dev; diff --git a/drivers/gpu/drm/lima/lima_mmu.h b/drivers/gpu/drm/lima/lima_mmu.h index 8c78319bcc8e..4f8ccbebcba1 100644 --- a/drivers/gpu/drm/lima/lima_mmu.h +++ b/drivers/gpu/drm/lima/lima_mmu.h @@ -10,6 +10,7 @@ struct lima_vm; int lima_mmu_init(struct lima_ip *ip); void lima_mmu_fini(struct lima_ip *ip); +void lima_mmu_flush_tlb(struct lima_ip *ip); void lima_mmu_switch_vm(struct lima_ip *ip, struct lima_vm *vm); void lima_mmu_page_fault_resume(struct lima_ip *ip); diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index a31a90c380b6..5f9c54475799 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -312,6 +312,26 @@ static const struct drm_sched_backend_ops lima_sched_ops = { .free_job = lima_sched_free_job, }; +static void lima_sched_recover_work(struct work_struct *work) +{ + struct lima_sched_pipe *pipe = + container_of(work, struct lima_sched_pipe, recover_work); + int i; + + for (i = 0; i < pipe->num_l2_cache; i++) + lima_l2_cache_flush(pipe->l2_cache[i]); + + if (pipe->bcast_mmu) { + lima_mmu_flush_tlb(pipe->bcast_mmu); + } else { + for (i = 0; i < pipe->num_mmu; i++) + lima_mmu_flush_tlb(pipe->mmu[i]); + } + + if (pipe->task_recover(pipe)) + drm_sched_fault(&pipe->base); +} + int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) { unsigned int timeout = lima_sched_timeout_ms > 0 ? @@ -320,6 +340,8 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) pipe->fence_context = dma_fence_context_alloc(1); spin_lock_init(&pipe->fence_lock); + INIT_WORK(&pipe->recover_work, lima_sched_recover_work); + return drm_sched_init(&pipe->base, &lima_sched_ops, 1, 0, msecs_to_jiffies(timeout), name); } @@ -331,11 +353,14 @@ void lima_sched_pipe_fini(struct lima_sched_pipe *pipe) void lima_sched_pipe_task_done(struct lima_sched_pipe *pipe) { - if (pipe->error) - drm_sched_fault(&pipe->base); - else { - struct lima_sched_task *task = pipe->current_task; - + struct lima_sched_task *task = pipe->current_task; + + if (pipe->error) { + if (task && task->recoverable) + schedule_work(&pipe->recover_work); + else + drm_sched_fault(&pipe->base); + } else { pipe->task_fini(pipe); dma_fence_signal(task->fence); } diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h index 1d814fecbcc0..d64393fb50a9 100644 --- a/drivers/gpu/drm/lima/lima_sched.h +++ b/drivers/gpu/drm/lima/lima_sched.h @@ -20,6 +20,9 @@ struct lima_sched_task { struct lima_bo **bos; int num_bos; + bool recoverable; + struct lima_bo *heap; + /* pipe fence */ struct dma_fence *fence; }; @@ -68,6 +71,9 @@ struct lima_sched_pipe { void (*task_fini)(struct lima_sched_pipe *pipe); void (*task_error)(struct lima_sched_pipe *pipe); void (*task_mmu_error)(struct lima_sched_pipe *pipe); + int (*task_recover)(struct lima_sched_pipe *pipe); + + struct work_struct recover_work; }; int lima_sched_task_init(struct lima_sched_task *task, -- 2.11.0