[PATCH RFC 060/111] staging: etnaviv: stop the hangcheck timer mis-firing

Lucas Stach l.stach at pengutronix.de
Thu Apr 2 08:30:02 PDT 2015


From: Russell King <rmk+kernel at arm.linux.org.uk>

If we queue up a large command buffer (32K) containing about 164 1080p
blit operations, it can take the GPU several seconds to complete before
raising the next event.  Our existing hangcheck code decides after a
second that the GPU is stuck, and provokes a retirement of the events.

This can lead to errors as the GPU isn't stuck - we could end up
overwriting the buffers which the GPU is currently executing.

Resolve this by also checking the current DMA address register, and
monitoring it for progress.  We have to be careful here, because if
we get stuck in a WAIT LINK, the DMA address will change by 16 bytes
(inclusive) while the GPU spins in the loop - even though we may not
have received the last event from the GPU (eg, because the PE is
busy.)

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 19 +++++++++++++++----
 drivers/staging/etnaviv/etnaviv_gpu.h |  1 +
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 24ed14804ebd..cd308976dec9 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -689,13 +689,24 @@ static void hangcheck_handler(unsigned long data)
 	struct drm_device *dev = gpu->drm;
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	uint32_t fence = gpu->retired_fence;
+	bool progress = false;
 
 	if (fence != gpu->hangcheck_fence) {
-		/* some progress has been made.. ya! */
-		gpu->hangcheck_fence = fence;
-	} else if (fence_after(gpu->submitted_fence, fence)) {
-		/* no progress and not done.. hung! */
 		gpu->hangcheck_fence = fence;
+		progress = true;
+	}
+
+	if (!progress) {
+		uint32_t dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+		int change = dma_addr - gpu->hangcheck_dma_addr;
+
+		if (change < 0 || change > 16) {
+			gpu->hangcheck_dma_addr = dma_addr;
+			progress = true;
+		}
+	}
+
+	if (!progress && fence_after(gpu->submitted_fence, fence)) {
 		dev_err(gpu->dev, "hangcheck detected gpu lockup!\n");
 		dev_err(gpu->dev, "     completed fence: %u\n", fence);
 		dev_err(gpu->dev, "     submitted fence: %u\n",
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 885eddf9fb1c..59dc9c1a048f 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -123,6 +123,7 @@ struct etnaviv_gpu {
 #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
 	struct timer_list hangcheck_timer;
 	uint32_t hangcheck_fence;
+	uint32_t hangcheck_dma_addr;
 	struct work_struct recover_work;
 };
 
-- 
2.1.4



More information about the dri-devel mailing list