drivers/gpu/drm/vc4/vc4_v3d.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
The hangcheck timer, which every submitted job arms and which queues
reset_work once a job stops making progress, and the job_done_work,
which the render-done interrupt queues to release completed jobs, are
never drained at teardown: vc4_irq_disable() cancels only
overflow_mem_work, and vc4_gem_destroy() runs from the drm-managed
release, after vc4_v3d_unbind() has already uninstalled the V3D
interrupt and cleared vc4->v3d.
A timer still armed by then reads V3D registers through the NULL
vc4->v3d pointer, and late callbacks run on the vc4_dev embedding
them after it has been freed.
Drain them in vc4_v3d_unbind(): shut the hangcheck timer down and
cancel reset_work before the interrupt is taken down, because
vc4_irq_reset() in a straggler reset re-enables it, then cancel
job_done_work once no source is left, before vc4->v3d is cleared.
This issue was found by an in-house static analysis tool.
Fixes: d5b1a78a772f ("drm/vc4: Add support for drawing 3D frames.")
Cc: stable@vger.kernel.org # 6.13+: vc4->gen does not exist on older trees
Assisted-by: Codex:gpt-5.6
Co-developed-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---
drivers/gpu/drm/vc4/vc4_v3d.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index f32410420d3e..11d4df3164b4 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -9,6 +9,7 @@
#include <linux/component.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/timer.h>
#include <drm/drm_print.h>
@@ -497,8 +498,18 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);
+ /* Kill the timer, then reset_work: vc4_irq_reset() re-enables it. */
+ if (vc4->gen == VC4_GEN_4) {
+ timer_shutdown_sync(&vc4->hangcheck.timer);
+ cancel_work_sync(&vc4->hangcheck.reset_work);
+ }
+
vc4_irq_uninstall(drm);
+ /* Nothing can queue job_done_work any more; drain it. */
+ if (vc4->gen == VC4_GEN_4)
+ cancel_work_sync(&vc4->job_done_work);
+
/* Disable the binner's overflow memory address, so the next
* driver probe (if any) doesn't try to reuse our old
* allocation.
Hi Fan Wu,
On 09/09/26 02:35, Fan Wu wrote:
> The hangcheck timer, which every submitted job arms and which queues
> reset_work once a job stops making progress, and the job_done_work,
> which the render-done interrupt queues to release completed jobs, are
> never drained at teardown: vc4_irq_disable() cancels only
> overflow_mem_work, and vc4_gem_destroy() runs from the drm-managed
> release, after vc4_v3d_unbind() has already uninstalled the V3D
> interrupt and cleared vc4->v3d.
>
> A timer still armed by then reads V3D registers through the NULL
> vc4->v3d pointer, and late callbacks run on the vc4_dev embedding
> them after it has been freed.
>
> Drain them in vc4_v3d_unbind(): shut the hangcheck timer down and
> cancel reset_work before the interrupt is taken down, because
> vc4_irq_reset() in a straggler reset re-enables it, then cancel
> job_done_work once no source is left, before vc4->v3d is cleared.
>
> This issue was found by an in-house static analysis tool.
>
> Fixes: d5b1a78a772f ("drm/vc4: Add support for drawing 3D frames.")
> Cc: stable@vger.kernel.org # 6.13+: vc4->gen does not exist on older trees
> Assisted-by: Codex:gpt-5.6
> Co-developed-by: Song Li <songl@zju.edu.cn>
> Signed-off-by: Song Li <songl@zju.edu.cn>
> Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
> ---
> drivers/gpu/drm/vc4/vc4_v3d.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
> index f32410420d3e..11d4df3164b4 100644
> --- a/drivers/gpu/drm/vc4/vc4_v3d.c
> +++ b/drivers/gpu/drm/vc4/vc4_v3d.c
> @@ -9,6 +9,7 @@
> #include <linux/component.h>
> #include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> +#include <linux/timer.h>
>
> #include <drm/drm_print.h>
>
> @@ -497,8 +498,18 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master,
> struct drm_device *drm = data;
> struct vc4_dev *vc4 = to_vc4_dev(drm);
>
> + /* Kill the timer, then reset_work: vc4_irq_reset() re-enables it. */
This comment provides no insight about the code, it's just describing
it.
> + if (vc4->gen == VC4_GEN_4) {
No need for this guards, as this function only runs on VC4_GEN_4.
> + timer_shutdown_sync(&vc4->hangcheck.timer);
> + cancel_work_sync(&vc4->hangcheck.reset_work);
> + }
> +
> vc4_irq_uninstall(drm);
>
> + /* Nothing can queue job_done_work any more; drain it. */
> + if (vc4->gen == VC4_GEN_4)
> + cancel_work_sync(&vc4->job_done_work);
Check Sashiko's feedback about this.
Best regards,
- Maíra
> +
> /* Disable the binner's overflow memory address, so the next
> * driver probe (if any) doesn't try to reuse our old
> * allocation.
>
© 2016 - 2026 Red Hat, Inc.