diff options
author | Eric Anholt <eric@anholt.net> | 2014-11-19 16:37:35 -0800 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2015-06-04 14:15:25 -0700 |
commit | 89717fa400c0a77850c7ca55ce43c4ca0085f548 (patch) | |
tree | 9a49158707fb6d1438cdcf822b47d0d8a18d0712 | |
parent | 6f8e8eb7f5008decbd69b3b14cd3fbe34113d319 (diff) | |
download | linux-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.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_gem.c | 71 |
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); +} |