From 2a36ed7bacd91e3bc8e9291da10462704af3003d Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Wed, 14 Jun 2017 16:25:11 +0300 Subject: video: tegra: host: Recover engine on abort Currently PVA cannot be recovered if there is a critical error (i.e. abort from the engine). This change modifies code to reset the PVA and abort existing work if: * PVA issues abort ISR * Communication through mailbox interface times out * Communication through CCQ interface times out JIRA PVA-51 Change-Id: I448d231737a122fe5bf6787e5fa1794c2a7b1a33 Signed-off-by: Arto Merilainen Reviewed-on: http://git-master/r/1502343 GVS: Gerrit_Virtual_Submit Reviewed-by: svccoveritychecker Reviewed-by: Vinod Gopalakrishnakurup --- drivers/video/tegra/host/nvhost_queue.c | 10 +++ drivers/video/tegra/host/nvhost_queue.h | 7 +++ drivers/video/tegra/host/pva/Makefile | 1 + drivers/video/tegra/host/pva/pva-interface.h | 1 + drivers/video/tegra/host/pva/pva.c | 19 +++--- drivers/video/tegra/host/pva/pva.h | 25 ++++++++ drivers/video/tegra/host/pva/pva_abort.c | 89 ++++++++++++++++++++++++++ drivers/video/tegra/host/pva/pva_ccq.c | 16 ++++- drivers/video/tegra/host/pva/pva_isr.c | 10 ++- drivers/video/tegra/host/pva/pva_mailbox.c | 44 ++++++++----- drivers/video/tegra/host/pva/pva_mailbox.h | 14 ++++- drivers/video/tegra/host/pva/pva_queue.c | 93 +++++++++++++++++++++++++++- drivers/video/tegra/host/pva/pva_queue.h | 3 +- 13 files changed, 299 insertions(+), 33 deletions(-) create mode 100644 drivers/video/tegra/host/pva/pva_abort.c diff --git a/drivers/video/tegra/host/nvhost_queue.c b/drivers/video/tegra/host/nvhost_queue.c index c9ba3cf02..03cf795eb 100644 --- a/drivers/video/tegra/host/nvhost_queue.c +++ b/drivers/video/tegra/host/nvhost_queue.c @@ -227,6 +227,16 @@ void nvhost_queue_deinit(struct nvhost_queue_pool *pool) pool = NULL; } +void nvhost_queue_abort_all(struct nvhost_queue_pool *pool) +{ + u32 id; + + mutex_lock(&pool->queue_lock); + for_each_set_bit(id, &pool->alloc_table, pool->max_queue_cnt) + nvhost_queue_abort(&pool->queues[id]); + mutex_unlock(&pool->queue_lock); +} + static void nvhost_queue_release(struct kref *ref) { struct nvhost_queue *queue = container_of(ref, struct nvhost_queue, diff --git a/drivers/video/tegra/host/nvhost_queue.h b/drivers/video/tegra/host/nvhost_queue.h index 510508d54..73b167ab9 100644 --- a/drivers/video/tegra/host/nvhost_queue.h +++ b/drivers/video/tegra/host/nvhost_queue.h @@ -184,6 +184,13 @@ struct nvhost_queue *nvhost_queue_alloc(struct nvhost_queue_pool *pool, unsigned int num_tasks, bool use_channel); +/** + * @brief Abort all active queues + * + * @param pool Pointer to a queue pool table + */ +void nvhost_queue_abort_all(struct nvhost_queue_pool *pool); + /** * @brief Abort tasks within a client queue * diff --git a/drivers/video/tegra/host/pva/Makefile b/drivers/video/tegra/host/pva/Makefile index a1503925b..517da1196 100644 --- a/drivers/video/tegra/host/pva/Makefile +++ b/drivers/video/tegra/host/pva/Makefile @@ -12,6 +12,7 @@ nvhost-pva-objs = \ pva_queue.o \ pva_debug.o \ pva_trace.o \ + pva_abort.o \ pva_ccq.o obj-$(CONFIG_TEGRA_GRHOST_PVA) += nvhost-pva.o diff --git a/drivers/video/tegra/host/pva/pva-interface.h b/drivers/video/tegra/host/pva/pva-interface.h index 2157ae1e2..d272ce6dc 100644 --- a/drivers/video/tegra/host/pva/pva-interface.h +++ b/drivers/video/tegra/host/pva/pva-interface.h @@ -90,6 +90,7 @@ #define PVA_AISR_LOGGING_OVERFLOW PVA_BIT(25) #define PVA_AISR_PRINTF_OVERFLOW PVA_BIT(24) #define PVA_AISR_CRASH_LOG PVA_BIT(23) +#define PVA_AISR_ABORT PVA_BIT(0) #define PVA_GET_ERROR_CODE(_s_) PVA_EXTRACT(_s_, 15, 0, enum pva_errors) diff --git a/drivers/video/tegra/host/pva/pva.c b/drivers/video/tegra/host/pva/pva.c index 028de9fdd..ddc09ca89 100644 --- a/drivers/video/tegra/host/pva/pva.c +++ b/drivers/video/tegra/host/pva/pva.c @@ -86,7 +86,6 @@ static int pva_init_fw(struct platform_device *pdev) struct pva_dma_alloc_info *priv2_buffer; u32 *ucode_ptr; int err = 0, w; - int timeout; u64 ucode_useg_addr; nvhost_dbg_fn(""); @@ -172,16 +171,7 @@ static int pva_init_fw(struct platform_device *pdev) nvhost_dbg_fn("Waiting for PVA to be READY"); /* Wait PVA to report itself as ready */ - if (tegra_platform_is_silicon()) { - timeout = wait_event_timeout(pva->mailbox_waitqueue, - pva->mailbox_status == PVA_MBOX_STATUS_DONE, - msecs_to_jiffies(60000)); - if (timeout <= 0) - err = -ETIMEDOUT; - - } else - wait_event(pva->mailbox_waitqueue, - pva->mailbox_status == PVA_MBOX_STATUS_DONE); + err = pva_mailbox_wait_event(pva, 60000); pva->mailbox_status = PVA_MBOX_STATUS_INVALID; @@ -422,6 +412,8 @@ int pva_finalize_poweron(struct platform_device *pdev) } } + pva->booted = true; + return err; err_poweron: @@ -434,6 +426,8 @@ int pva_prepare_poweroff(struct platform_device *pdev) struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct pva *pva = pdata->private_data; + pva->booted = false; + disable_irq(pva->irq); pva_free_fw(pdev, pva); @@ -477,6 +471,7 @@ static int pva_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pdata); init_waitqueue_head(&pva->mailbox_waitqueue); mutex_init(&pva->mailbox_mutex); + mutex_init(&pva->ccq_mutex); pva->submit_mode = PVA_SUBMIT_MODE_MAILBOX; /* Map MMIO range to kernel space */ @@ -508,6 +503,8 @@ static int pva_probe(struct platform_device *pdev) if (err < 0) goto err_isr_init; + pva_abort_init(pva); + err = nvhost_syncpt_unit_interface_init(pdev); if (err) goto err_mss_init; diff --git a/drivers/video/tegra/host/pva/pva.h b/drivers/video/tegra/host/pva/pva.h index e1bdba1b2..1103513f1 100644 --- a/drivers/video/tegra/host/pva/pva.h +++ b/drivers/video/tegra/host/pva/pva.h @@ -154,6 +154,8 @@ struct pva { enum pva_mailbox_status mailbox_status; struct mutex mailbox_mutex; + struct mutex ccq_mutex; + struct pva_crashdump_debugfs_entry debugfs_entry_r5; struct pva_crashdump_debugfs_entry debugfs_entry_vpu0; struct pva_crashdump_debugfs_entry debugfs_entry_vpu1; @@ -163,6 +165,9 @@ struct pva { struct pva_trace_log pva_trace; u32 submit_mode; + + struct work_struct pva_abort_handler_work; + bool booted; }; /** @@ -215,6 +220,7 @@ int pva_prepare_poweroff(struct platform_device *pdev); * */ int pva_register_isr(struct platform_device *dev); + /** * @brief Initiallze pva debug utils * @@ -223,4 +229,23 @@ int pva_register_isr(struct platform_device *dev); * */ void pva_debugfs_init(struct platform_device *pdev); + +/** + * @brief Initiallze PVA abort handler + * + * @param pva Pointer to PVA structure + * @return none + * + */ +void pva_abort_init(struct pva *pva); + +/** + * @brief Recover PVA back into working state + * + * @param pva Pointer to PVA structure + * @return none + * + */ +void pva_abort(struct pva *pva); + #endif diff --git a/drivers/video/tegra/host/pva/pva_abort.c b/drivers/video/tegra/host/pva/pva_abort.c new file mode 100644 index 000000000..5db65ca89 --- /dev/null +++ b/drivers/video/tegra/host/pva/pva_abort.c @@ -0,0 +1,89 @@ +/* + * PVA abort handler + * + * Copyright (c) 2017, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "nvhost_acm.h" +#include "dev.h" +#include "pva.h" + +static void pva_abort_handler(struct work_struct *work) +{ + struct pva *pva = container_of(work, struct pva, + pva_abort_handler_work); + struct platform_device *pdev = pva->pdev; + + /* Dump nvhost state to show the pending jobs */ + nvhost_debug_dump_device(pdev); + + /* First, lock mailbox mutex to avoid synchronous communication. */ + do { + if (pva->mailbox_status == PVA_MBOX_STATUS_WFI) { + pva->mailbox_status = PVA_MBOX_STATUS_ABORTED; + wake_up(&pva->mailbox_waitqueue); + schedule(); + } + } while (mutex_trylock(&pva->mailbox_mutex) == false); + + /* There is no ongoing activity anymore. Update mailbox status */ + pva->mailbox_status = PVA_MBOX_STATUS_INVALID; + + /* Lock CCQ mutex to avoid asynchornous communication */ + mutex_lock(&pva->ccq_mutex); + + /* + * If boot was still on-going, skip over recovery and let boot-up + * routine handle the failure + */ + if (!pva->booted) { + nvhost_warn(&pdev->dev, "Recovery skipped: PVA is not booted"); + goto skip_recovery; + } + + /* + * If we use channel submit mode, nvhost handles the channel + * clean-up and syncpoint increments + */ + if (pva->submit_mode == PVA_SUBMIT_MODE_CHANNEL_CCQ) { + nvhost_warn(&pdev->dev, "Recovery skipped: Submit mode does not require clean-up"); + goto skip_recovery; + } + + /* Reset the PVA and reload firmware */ + nvhost_module_reset(pdev, true); + + /* Remove pending tasks from the queue */ + nvhost_queue_abort_all(pva->pool); + + nvhost_warn(&pdev->dev, "Recovery finished"); + +skip_recovery: + mutex_unlock(&pva->ccq_mutex); + mutex_unlock(&pva->mailbox_mutex); +} + +void pva_abort(struct pva *pva) +{ + WARN(true, "Attempting to recover the engine"); + schedule_work(&pva->pva_abort_handler_work); +} + +void pva_abort_init(struct pva *pva) +{ + INIT_WORK(&pva->pva_abort_handler_work, pva_abort_handler); +} diff --git a/drivers/video/tegra/host/pva/pva_ccq.c b/drivers/video/tegra/host/pva/pva_ccq.c index d41d7b15a..980c5e303 100644 --- a/drivers/video/tegra/host/pva/pva_ccq.c +++ b/drivers/video/tegra/host/pva/pva_ccq.c @@ -55,15 +55,25 @@ static int pva_ccq_wait(struct pva *pva, int timeout) int pva_ccq_send(struct pva *pva, u64 cmd) { - int err; + int err = 0; + + mutex_lock(&pva->ccq_mutex); err = pva_ccq_wait(pva, 100); if (err < 0) - return err; + goto err_wait_ccq; /* Make the writes to CCQ */ host1x_writel(pva->pdev, cfg_ccq_r(), (u32)(cmd >> 32)); host1x_writel(pva->pdev, cfg_ccq_r(), (u32)(cmd & 0xffffffff)); - return 0; + mutex_unlock(&pva->ccq_mutex); + + return err; + +err_wait_ccq: + mutex_unlock(&pva->ccq_mutex); + pva_abort(pva); + + return err; } diff --git a/drivers/video/tegra/host/pva/pva_isr.c b/drivers/video/tegra/host/pva/pva_isr.c index 69c6ba6af..cd9c22967 100644 --- a/drivers/video/tegra/host/pva/pva_isr.c +++ b/drivers/video/tegra/host/pva/pva_isr.c @@ -1,7 +1,7 @@ /* * PVA ISR code for T194 * - * Copyright (c) 2016, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2016-2017, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -32,6 +32,7 @@ static irqreturn_t pva_isr(int irq, void *dev_id) u32 status7 = host1x_readl(pdev, hsp_sm7_r()); u32 status6 = host1x_readl(pdev, hsp_sm6_r()); u32 status5 = host1x_readl(pdev, hsp_sm5_r()); + bool recover = false; if (status5 & PVA_AISR_INT_PENDING) { nvhost_dbg_info("PVA AISR (%x)", status7); @@ -48,6 +49,10 @@ static irqreturn_t pva_isr(int irq, void *dev_id) nvhost_warn(&pdev->dev, "PVA AISR: PVA_AISR_PRINTF_OVERFLOW"); if (status5 & PVA_AISR_CRASH_LOG) nvhost_warn(&pdev->dev, "PVA AISR: PVA_AISR_CRASH_LOG"); + if (status5 & PVA_AISR_ABORT) { + nvhost_warn(&pdev->dev, "PVA AISR: PVA_AISR_ABORT"); + recover = true; + } host1x_writel(pdev, hsp_sm5_r(), 0x0); } @@ -72,6 +77,9 @@ static irqreturn_t pva_isr(int irq, void *dev_id) /* Copy trace points to ftrace buffer */ pva_trace_copy_to_ftrace(pva); + if (recover) + pva_abort(pva); + return IRQ_HANDLED; } diff --git a/drivers/video/tegra/host/pva/pva_mailbox.c b/drivers/video/tegra/host/pva/pva_mailbox.c index 2d8b49261..56e6c64b1 100644 --- a/drivers/video/tegra/host/pva/pva_mailbox.c +++ b/drivers/video/tegra/host/pva/pva_mailbox.c @@ -69,6 +69,33 @@ static int pva_mailbox_send_cmd(struct pva *pva, struct pva_cmd *cmd, return 0; } +int pva_mailbox_wait_event(struct pva *pva, int wait_time) +{ + int timeout = 1; + int err; + + /* Wait for the event being triggered in ISR */ + if (tegra_platform_is_silicon()) + timeout = wait_event_timeout(pva->mailbox_waitqueue, + pva->mailbox_status == PVA_MBOX_STATUS_DONE || + pva->mailbox_status == PVA_MBOX_STATUS_ABORTED, + msecs_to_jiffies(wait_time)); + else + wait_event(pva->mailbox_waitqueue, + pva->mailbox_status == PVA_MBOX_STATUS_DONE || + pva->mailbox_status == PVA_MBOX_STATUS_ABORTED); + + if (timeout <= 0) { + err = -ETIMEDOUT; + pva_abort(pva); + } else if (pva->mailbox_status == PVA_MBOX_STATUS_ABORTED) + err = -EIO; + else + err = 0; + + return err; +} + void pva_mailbox_isr(struct pva *pva) { struct platform_device *pdev = pva->pdev; @@ -118,7 +145,6 @@ int pva_mailbox_send_cmd_sync(struct pva *pva, struct pva_cmd *cmd, u32 nregs, struct pva_mailbox_status_regs *mailbox_status_regs) { - int timeout; int err = 0; if (mailbox_status_regs == NULL) { @@ -143,19 +169,9 @@ int pva_mailbox_send_cmd_sync(struct pva *pva, if (err < 0) goto err_send_command; - /* Wait for the event being triggered in ISR */ - if (tegra_platform_is_silicon()) { - timeout = wait_event_timeout(pva->mailbox_waitqueue, - pva->mailbox_status == PVA_MBOX_STATUS_DONE, - msecs_to_jiffies(100)); - if (timeout <= 0) { - err = -EBUSY; - goto err_wait_response; - } - } else { - wait_event(pva->mailbox_waitqueue, - pva->mailbox_status == PVA_MBOX_STATUS_DONE); - } + err = pva_mailbox_wait_event(pva, 100); + if (err < 0) + goto err_wait_response; /* Return interrupt status back to caller */ memcpy(mailbox_status_regs, &pva->mailbox_status_regs, diff --git a/drivers/video/tegra/host/pva/pva_mailbox.h b/drivers/video/tegra/host/pva/pva_mailbox.h index ad4bc9eb5..f54019b64 100644 --- a/drivers/video/tegra/host/pva/pva_mailbox.h +++ b/drivers/video/tegra/host/pva/pva_mailbox.h @@ -1,7 +1,7 @@ /* * PVA mailbox header * - * Copyright (c) 2016, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2016-2017, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -51,6 +51,7 @@ enum pva_mailbox_status { PVA_MBOX_STATUS_INVALID = 0, PVA_MBOX_STATUS_WFI = 1, PVA_MBOX_STATUS_DONE = 2, + PVA_MBOX_STATUS_ABORTED = 3, }; /** @@ -98,4 +99,15 @@ int pva_mailbox_send_cmd_sync(struct pva *pva, */ void pva_mailbox_isr(struct pva *pva); +/** + * pva_mailbox_wait_event() - mailbox wait event + * + * @pva:» Pointer to PVA structure + * @wait_time» WaitTime Interval for the event + * + * This function do the wait until the mailbox isr get invoked based on + * the mailbox register set by the ucode. + */ +int pva_mailbox_wait_event(struct pva *pva, int wait_time); + #endif /*__PVA_MAINBOX_H__*/ diff --git a/drivers/video/tegra/host/pva/pva_queue.c b/drivers/video/tegra/host/pva/pva_queue.c index 461f2bdc8..99071d2ce 100644 --- a/drivers/video/tegra/host/pva/pva_queue.c +++ b/drivers/video/tegra/host/pva/pva_queue.c @@ -23,6 +23,8 @@ #include #include +#include + #include #include "nvhost_syncpt_unit_interface.h" @@ -939,7 +941,8 @@ static void pva_task_update(void *priv, int nr_completed) /* Unpin job memory. PVA shouldn't be using it anymore */ pva_task_unpin_mem(task); - pva_completed_task_status(task->pva); + if (!task->invalid) + pva_completed_task_status(task->pva); /* Drop PM runtime reference of PVA */ nvhost_module_idle(task->pva->pdev); @@ -1135,6 +1138,8 @@ static int pva_task_submit(struct pva_submit_task *task) if (err < 0) goto err_submit; + task->syncpt_thresh = thresh; + err = nvhost_intr_register_notifier(host1x_pdev, queue->syncpt_id, thresh, pva_task_update, task); @@ -1253,13 +1258,97 @@ static int pva_queue_set_attribute(struct nvhost_queue *queue, void *args) return err; } +static void pva_queue_cleanup_fence(struct pva_fence *fence, + struct pva_parameter_ext *fence_ext) +{ + struct dma_buf *dmabuf; + u8 *dmabuf_cpuva; + u32 *fence_cpuva; + + if (fence->type != PVA_FENCE_TYPE_SEMAPHORE) + return; + + dmabuf = fence_ext->dmabuf; + dmabuf_cpuva = dma_buf_vmap(dmabuf); + + if (!dmabuf_cpuva) + return; + + if (!(fence->semaphore_offset % 4)) + return; + + fence_cpuva = (void *)&dmabuf_cpuva[fence->semaphore_offset]; + *fence_cpuva = fence->semaphore_value; + + dma_buf_vunmap(dmabuf, dmabuf_cpuva); +} + +static void pva_queue_cleanup_status(struct pva_status_handle *status_h, + struct pva_parameter_ext *status_h_ext) +{ + struct dma_buf *dmabuf = status_h_ext->dmabuf; + u8 *dmabuf_cpuva = dma_buf_vmap(dmabuf); + struct nvhost_notification *status_ptr; + + if (!dmabuf_cpuva) + return; + + status_ptr = (void *)&dmabuf_cpuva[status_h->offset]; + status_ptr->status = 0x8888; + + dma_buf_vunmap(dmabuf, dmabuf_cpuva); +} + +static void pva_queue_cleanup(struct nvhost_queue *queue, + struct pva_submit_task *task) +{ + struct platform_device *pdev = queue->pool->pdev; + struct nvhost_master *host = nvhost_get_host(pdev); + bool expired = nvhost_syncpt_is_expired(&host->syncpt, + queue->syncpt_id, + task->syncpt_thresh); + unsigned int i; + + /* + * Ensure that there won't be communication with PVA for + * checking the task status + */ + task->invalid = true; + + /* Ignore expired fences */ + if (expired) + return; + + /* Write task status first */ + for (i = 0; i < task->num_output_task_status; i++) + pva_queue_cleanup_status(task->output_task_status, + task->output_task_status_ext); + + /* Finish up non-syncpoint fences */ + for (i = 0; i < task->num_postfences; i++) + pva_queue_cleanup_fence(task->postfences + i, + task->postfences_sema_ext + i); + + /* Finish syncpoint increments to release waiters */ + nvhost_syncpt_cpu_incr_ext(pdev, queue->syncpt_id); +} + static int pva_queue_abort(struct nvhost_queue *queue) { - /* TBD: Abort pending tasks from the queue */ + struct pva_submit_task *task; + + mutex_lock(&queue->list_lock); + + list_for_each_entry(task, &queue->tasklist, node) + pva_queue_cleanup(queue, task); + + mutex_unlock(&queue->list_lock); return 0; } + + struct nvhost_queue_ops pva_queue_ops = { .abort = pva_queue_abort, .submit = pva_queue_submit, diff --git a/drivers/video/tegra/host/pva/pva_queue.h b/drivers/video/tegra/host/pva/pva_queue.h index be7455bec..1bdb0352b 100644 --- a/drivers/video/tegra/host/pva/pva_queue.h +++ b/drivers/video/tegra/host/pva/pva_queue.h @@ -83,7 +83,6 @@ struct pva_submit_task { dma_addr_t dma_addr; void *va; int pool_index; - u32 *postfence_va; u8 num_prefences; u8 num_postfences; @@ -93,6 +92,8 @@ struct pva_submit_task { u8 num_output_task_status; u32 operation; u64 timeout; + bool invalid; + u32 syncpt_thresh; /* Data provided by userspace "as is" */ struct pva_fence prefences[PVA_MAX_PREFENCES]; -- cgit v1.2.2