summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2014-11-19 16:37:35 -0800
committerEric Anholt <eric@anholt.net>2015-06-04 14:15:25 -0700
commit89717fa400c0a77850c7ca55ce43c4ca0085f548 (patch)
tree9a49158707fb6d1438cdcf822b47d0d8a18d0712
parent6f8e8eb7f5008decbd69b3b14cd3fbe34113d319 (diff)
downloadlinux-89717fa400c0a77850c7ca55ce43c4ca0085f548.tar.gz
drm/vc4: Move the hang detection to a timer.
We're going to need to do this for async execution, so make the switch now. Since the detection is smarter now (it can see when you're making progess through the lists), we can crank up the default timeout for executing a job. v2: Move vc4_gem_init() out from in between drm_irq_install() and its error check. v3: Rebase on the KMS work. Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c4
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h7
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c71
3 files changed, 76 insertions, 6 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 433be8c09efa..bc1e54ca207a 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -58,8 +58,6 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags)
if (!vc4)
return -ENOMEM;
- INIT_LIST_HEAD(&vc4->overflow_list);
-
vc4->firmware_node = of_parse_phandle(dev->dev->of_node, "firmware", 0);
if (!vc4->firmware_node) {
DRM_ERROR("Failed to parse firmware node.\n");
@@ -81,6 +79,8 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
return ret;
+ vc4_gem_init(dev);
+
vc4_kms_load(dev);
return 0;
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 5e69d7611d26..f8142ab244a1 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -28,6 +28,12 @@ struct vc4_dev {
*/
struct list_head overflow_list;
struct work_struct overflow_mem_work;
+
+ struct {
+ uint32_t last_ct0ca, last_ct1ca;
+ struct timer_list timer;
+ struct work_struct reset_work;
+ } hangcheck;
};
static inline struct vc4_dev *
@@ -276,6 +282,7 @@ void vc4_debugfs_cleanup(struct drm_minor *minor);
void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
/* vc4_gem.c */
+void vc4_gem_init(struct drm_device *dev);
int vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index abfc242b8025..1ed42e8e1fd1 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -31,6 +31,15 @@
#include "vc4_regs.h"
static void
+vc4_queue_hangcheck(struct drm_device *dev)
+{
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+ mod_timer(&vc4->hangcheck.timer,
+ round_jiffies_up(jiffies + msecs_to_jiffies(100)));
+}
+
+static void
vc4_reset(struct drm_device *dev)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
@@ -43,6 +52,47 @@ vc4_reset(struct drm_device *dev)
}
static void
+vc4_reset_work(struct work_struct *work)
+{
+ struct vc4_dev *vc4 =
+ container_of(work, struct vc4_dev, hangcheck.reset_work);
+
+ vc4_reset(vc4->dev);
+}
+
+static void
+vc4_hangcheck_elapsed(unsigned long data)
+{
+ struct drm_device *dev = (struct drm_device *)data;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ uint32_t ct0ca, ct1ca;
+
+ /* If idle, we can stop watching for hangs. */
+ if (vc4->frame_done)
+ return;
+
+ ct0ca = V3D_READ(V3D_CTNCA(0));
+ ct1ca = V3D_READ(V3D_CTNCA(1));
+
+ /* If we've made any progress in execution, rearm the timer
+ * and wait.
+ */
+ if (ct0ca != vc4->hangcheck.last_ct0ca ||
+ ct1ca != vc4->hangcheck.last_ct1ca) {
+ vc4->hangcheck.last_ct0ca = ct0ca;
+ vc4->hangcheck.last_ct1ca = ct1ca;
+ vc4_queue_hangcheck(dev);
+ return;
+ }
+
+ /* We've gone too long with no progress, reset. This has to
+ * be done from a work struct, since resetting can sleep and
+ * this timer hook isn't allowed to.
+ */
+ schedule_work(&vc4->hangcheck.reset_work);
+}
+
+static void
submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
@@ -145,7 +195,9 @@ vc4_submit(struct drm_device *dev, struct vc4_exec_info *exec)
submit_cl(dev, 0, ct0ca, ct0ea);
submit_cl(dev, 1, ct1ca, ct1ea);
- ret = vc4_wait_for_job(dev, exec, 1000);
+ vc4_queue_hangcheck(dev);
+
+ ret = vc4_wait_for_job(dev, exec, 10000);
if (ret)
return ret;
@@ -360,10 +412,8 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
goto fail;
ret = vc4_submit(dev, &exec);
- if (ret) {
- vc4_reset(dev);
+ if (ret)
goto fail;
- }
fail:
if (exec.bo) {
@@ -395,3 +445,16 @@ fail:
return ret;
}
+
+void
+vc4_gem_init(struct drm_device *dev)
+{
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+ INIT_LIST_HEAD(&vc4->overflow_list);
+
+ INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work);
+ setup_timer(&vc4->hangcheck.timer,
+ vc4_hangcheck_elapsed,
+ (unsigned long) dev);
+}