[PATCH RFC 011/111] staging: etnaviv: fix fence implementation

Lucas Stach l.stach at pengutronix.de
Thu Apr 2 08:29:13 PDT 2015


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

The fence implementation relied upon incrementing a 32-bit number,
and using unsigned comparisons.  This is a limited number space,
which, when exhausted will lead to unpredictable behaviour.  Turn
this into a circular namespace, and use signed difference comparisons.

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 drivers/staging/etnaviv/etnaviv_drv.c |  5 +++--
 drivers/staging/etnaviv/etnaviv_drv.h | 14 +++++++++++++-
 drivers/staging/etnaviv/etnaviv_gpu.c |  4 ++--
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index da7035ce07a2..cc860b63447f 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -303,7 +303,7 @@ int etnaviv_wait_fence_interruptable(struct drm_device *dev, uint32_t pipe,
 	if (!gpu)
 		return -ENXIO;
 
-	if (fence > gpu->submitted_fence) {
+	if (fence_after(fence, gpu->submitted_fence)) {
 		DRM_ERROR("waiting on invalid fence: %u (of %u)\n",
 				fence, gpu->submitted_fence);
 		return -EINVAL;
@@ -344,7 +344,8 @@ void etnaviv_update_fence(struct drm_device *dev, uint32_t fence)
 	struct etnaviv_drm_private *priv = dev->dev_private;
 
 	mutex_lock(&dev->struct_mutex);
-	priv->completed_fence = max(fence, priv->completed_fence);
+	if (fence_after(fence, priv->completed_fence))
+		priv->completed_fence = fence;
 	mutex_unlock(&dev->struct_mutex);
 
 	wake_up_all(&priv->fence_event);
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 63994f22d8c9..bf5d1d9cc891 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -127,10 +127,22 @@ u32 etnaviv_readl(const void __iomem *addr);
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
+/* returns true if fence a comes after fence b */
+static inline bool fence_after(uint32_t a, uint32_t b)
+{
+	return (int32_t)(a - b) > 0;
+}
+
+static inline bool fence_after_eq(uint32_t a, uint32_t b)
+{
+	return (int32_t)(a - b) >= 0;
+}
+
 static inline bool fence_completed(struct drm_device *dev, uint32_t fence)
 {
 	struct etnaviv_drm_private *priv = dev->dev_private;
-	return priv->completed_fence >= fence;
+
+	return fence_after_eq(priv->completed_fence, fence);
 }
 
 static inline int align_pitch(int width, int bpp)
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index df5bef16ff4c..859edcccdda6 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -648,7 +648,7 @@ static void hangcheck_handler(unsigned long data)
 	if (fence != gpu->hangcheck_fence) {
 		/* some progress has been made.. ya! */
 		gpu->hangcheck_fence = fence;
-	} else if (fence < gpu->submitted_fence) {
+	} else if (fence_after(gpu->submitted_fence, fence)) {
 		/* no progress and not done.. hung! */
 		gpu->hangcheck_fence = fence;
 		dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n",
@@ -661,7 +661,7 @@ static void hangcheck_handler(unsigned long data)
 	}
 
 	/* if still more pending work, reset the hangcheck timer: */
-	if (gpu->submitted_fence > gpu->hangcheck_fence)
+	if (fence_after(gpu->submitted_fence, gpu->hangcheck_fence))
 		hangcheck_timer_reset(gpu);
 }
 
-- 
2.1.4



More information about the dri-devel mailing list