* [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
@ 2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown Anand Moon
` (17 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko
The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.
Fix the lifetime by moving v4l2_m2m_init() into vdec_probe() and
releasing it in vdec_remove() which resolves passing the global
core->m2m_dev down to session contexts so all sessions share the
same hardware instance.
This change aligns the driver with proper v4l2_m2m usage, ensuring
the hardware device lifetime is tied to the platform driver core,
not individual sessions, and making teardown safe and predictable.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260520045905.6ACBA1F000E9@smtp.kernel.org/#t
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 36 +++++++++++++------------
drivers/staging/media/meson/vdec/vdec.h | 4 +--
2 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe5..6ae3471155a87 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -153,7 +153,7 @@ static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
- v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
+ v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
static const struct v4l2_m2m_ops vdec_m2m_ops = {
@@ -873,23 +873,16 @@ static int vdec_open(struct file *file)
sess->core = core;
- sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
- if (IS_ERR(sess->m2m_dev)) {
- dev_err(dev, "Fail to v4l2_m2m_init\n");
- ret = PTR_ERR(sess->m2m_dev);
- goto err_free_sess;
- }
-
- sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init);
+ sess->m2m_ctx = v4l2_m2m_ctx_init(core->m2m_dev, sess, m2m_queue_init);
if (IS_ERR(sess->m2m_ctx)) {
dev_err(dev, "Fail to v4l2_m2m_ctx_init\n");
ret = PTR_ERR(sess->m2m_ctx);
- goto err_m2m_release;
+ goto err_free_sess;
}
ret = vdec_init_ctrls(sess);
if (ret)
- goto err_m2m_ctx_release;
+ goto err_release_ctx;
sess->pixfmt_cap = formats[0].pixfmts_cap[0];
sess->fmt_out = &formats[0];
@@ -913,10 +906,8 @@ static int vdec_open(struct file *file)
return 0;
-err_m2m_ctx_release:
+err_release_ctx:
v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
- v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
kfree(sess);
return ret;
@@ -927,9 +918,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
v4l2_m2m_ctx_release(sess->m2m_ctx);
- v4l2_m2m_release(sess->m2m_dev);
v4l2_fh_del(&sess->fh, file);
v4l2_fh_exit(&sess->fh);
+ v4l2_ctrl_handler_free(&sess->ctrl_handler);
mutex_destroy(&sess->lock);
mutex_destroy(&sess->bufs_recycle_lock);
@@ -1059,16 +1050,23 @@ static int vdec_probe(struct platform_device *pdev)
if (ret)
return ret;
+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(core->m2m_dev)) {
+ dev_err(dev, "Failed to initialize v4l2 m2m device\n");
+ return PTR_ERR(core->m2m_dev);
+ }
+
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret) {
dev_err(dev, "Couldn't register v4l2 device\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_m2m_release;
}
vdev = video_device_alloc();
if (!vdev) {
ret = -ENOMEM;
- goto err_vdev_release;
+ goto err_v4l2_unregister;
}
core->vdev_dec = vdev;
@@ -1096,7 +1094,10 @@ static int vdec_probe(struct platform_device *pdev)
err_vdev_release:
video_device_release(vdev);
+err_v4l2_unregister:
v4l2_device_unregister(&core->v4l2_dev);
+err_m2m_release:
+ v4l2_m2m_release(core->m2m_dev);
return ret;
}
@@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
struct amvdec_core *core = platform_get_drvdata(pdev);
video_unregister_device(core->vdev_dec);
+ v4l2_m2m_release(core->m2m_dev);
v4l2_device_unregister(&core->v4l2_dev);
}
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index 7a5d8e871d708..cc0cfafb8a951 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -63,6 +63,7 @@ struct amvdec_session;
* @vdec_hevcf_clk: VDEC_HEVCF clock
* @esparser_reset: RESET for the PARSER
* @vdev_dec: video device for the decoder
+ * @m2m_dev: v4l2 m2m device
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
@@ -87,6 +88,7 @@ struct amvdec_core {
struct reset_control *esparser_reset;
struct video_device *vdev_dec;
+ struct v4l2_m2m_dev *m2m_dev;
struct v4l2_device v4l2_dev;
struct amvdec_session *cur_sess;
@@ -183,7 +185,6 @@ enum amvdec_status {
*
* @core: reference to the vdec core struct
* @fh: v4l2 file handle
- * @m2m_dev: v4l2 m2m device
* @m2m_ctx: v4l2 m2m context
* @ctrl_handler: V4L2 control handler
* @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
@@ -230,7 +231,6 @@ struct amvdec_session {
struct amvdec_core *core;
struct v4l2_fh fh;
- struct v4l2_m2m_dev *m2m_dev;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *ctrl_min_buf_capture;
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
2026-07-13 12:06 ` [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path Anand Moon
@ 2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread Anand Moon
` (16 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
The vdec driver suffered from unsafe state transitions and race
conditions when handling concurrent STREAMON / STREAMOFF calls and
dynamic resolution change (DRC) events. Global context pointers
(e.g. core->cur_sess) and session status flags were updated outside
proper lock boundaries, allowing parallel threads to corrupt hardware
state under load.
Address these architectural stability flaws with the following changes:
1. In vdec_start_streaming(), safely encapsulate hardware occupancy
evaluations and target session context claims within a secure mutex
lock (core->lock). This blocks overlapping multi-threaded STREAMON
calls from creating concurrent assignment conflicts.
2. Restructure initialization error path handlers to avoid global memory
maps cross-contamination. Segregate buffer flushes cleanly based
strictly on the active vb2 queue type (OUTPUT vs CAPTURE) to avoid
incorrectly reclaiming undecoded ready blocks.
3. In vdec_stop_streaming(), introduce state-aware tracking to
accurately identify DRC conditions. If a resolution modification
forces a capture queue reset while the output stream is still live,
preserve the driver's inner runtime states and bypass premature
hardware power-off sweeps.
4. Enforce strict null-pointer safety limits inside DMA unmapping
sequences. Explicitly clear tracking metadata entries
(sess->vififo_vaddr = NULL) inside the VIFIFO freeing routines to
neutralize accidental double-free risks.
Together these changes harden the driver against concurrency bugs,
eliminate memory leaks, and ensure predictable session lifecycle
management under multi threaded workloads.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 136 +++++++++++++++++-------
1 file changed, 95 insertions(+), 41 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6ae3471155a87..d1f35fc893de1 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -286,11 +286,6 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct vb2_v4l2_buffer *buf;
int ret;
- if (core->cur_sess && core->cur_sess != sess) {
- ret = -EBUSY;
- goto bufs_done;
- }
-
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
}
if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_NEEDS_RESUME ||
- sess->status == STATUS_INIT)
+ sess->status == STATUS_NEEDS_RESUME)
+ return 0;
+
+ /*
+ * Secure the core hardware lock before checking availability
+ * and updating session states to prevent STREAMON race conditions.
+ */
+ mutex_lock(&core->lock);
+ if (core->cur_sess && core->cur_sess != sess) {
+ ret = -EBUSY;
+ mutex_unlock(&core->lock);
+ goto err_unlock_no_hw;
+ }
+
+ /* If already half-initialized, do not re-initialize */
+ if (sess->status == STATUS_INIT) {
+ mutex_unlock(&core->lock);
return 0;
+ }
+
+ sess->status = STATUS_INIT;
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
sess->vififo_size = SIZE_VIFIFO;
sess->vififo_vaddr =
@@ -319,7 +334,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (!sess->vififo_vaddr) {
dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
- goto bufs_done;
+ goto err_cleanup_session;
}
sess->should_stop = 0;
@@ -333,33 +348,43 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
ret = vdec_poweron(sess);
if (ret)
- goto vififo_free;
+ goto err_free_vififo;
sess->sequence_cap = 0;
sess->sequence_out = 0;
+
if (vdec_codec_needs_recycle(sess))
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
- sess->status = STATUS_INIT;
- core->cur_sess = sess;
schedule_work(&sess->esparser_queue_work);
return 0;
-vififo_free:
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
-bufs_done:
- while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
- while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
-
+err_free_vififo:
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+err_cleanup_session:
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 0;
else
sess->streamon_cap = 0;
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ core->cur_sess = NULL;
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ mutex_unlock(&core->lock);
+err_unlock_no_hw:
+ while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+ while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+
return ret;
}
@@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q)
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
struct vb2_v4l2_buffer *buf;
+ enum amvdec_status old_status;
+ bool full_cleanup = false;
- if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_INIT ||
- (sess->status == STATUS_NEEDS_RESUME &&
- (!sess->streamon_out || !sess->streamon_cap))) {
- if (vdec_codec_needs_recycle(sess))
- kthread_stop(sess->recycle_thread);
-
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- kfree(sess->priv);
- sess->priv = NULL;
- core->cur_sess = NULL;
- sess->status = STATUS_STOPPED;
- }
-
+ /* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_out = 0;
} else {
/* Drain remaining refs if was still running */
@@ -431,9 +439,55 @@ static void vdec_stop_streaming(struct vb2_queue *q)
while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_cap = 0;
}
+
+ /* Hold core lock continuously for the state and resource processing */
+ mutex_lock(&core->lock);
+ old_status = sess->status;
+
+ if (old_status == STATUS_RUNNING || old_status == STATUS_INIT ||
+ (old_status == STATUS_NEEDS_RESUME && (!sess->streamon_out ||
+ !sess->streamon_cap))) {
+ /*
+ * If it's a DRC event (Capture queue streamoff only), preserve
+ * the status
+ */
+ if (old_status == STATUS_NEEDS_RESUME && sess->streamon_out) {
+ full_cleanup = false;
+ } else {
+ full_cleanup = true;
+ sess->status = STATUS_STOPPED;
+ }
+ }
+
+ if (full_cleanup) {
+ if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
+ kthread_stop(sess->recycle_thread);
+ }
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ core->cur_sess = NULL;
+
+ kfree(sess->priv);
+ sess->priv = NULL;
+ } else {
+ if (sess->status == STATUS_NEEDS_RESUME)
+ sess->changed_format = 0;
+ }
+ mutex_unlock(&core->lock);
}
static int vdec_vb2_buf_prepare(struct vb2_buffer *vb)
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
2026-07-13 12:06 ` [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path Anand Moon
2026-07-13 12:06 ` [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown Anand Moon
@ 2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines Anand Moon
` (15 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Validate the return status of kthread_run() during decoder recycling
thread initialization to prevent memory leaks, and enforce robust state
checking before invoking kthread_stop() to prevent kernel panics.
If the system operates under severe memory constraints, the previous
implementation silently accepted error pointers returned by a failed
kthread_run() operation. Attempting to pass these invalid error pointers
downstream to kthread_stop() during streaming stop or application
teardown routines triggered instant kernel panics.
Furthermore, when thread initialization failed, the lack of an explicit
unwinding path leaked the session's private context (sess->priv) and its
associated firmware memory block allocations. Because sess->status was
never transitioned to STATUS_INIT, any subsequent call to stop streaming
or close the driver file node failed its state validation checks,
permanently orphaning active platform DMA assets and codec parameters.
Fix these thread lifetime and memory leak bugs via the following:
1. In vdec_start_streaming(), add an explicit IS_ERR() verification
barrier immediately following the recycling kthread launch. If thread
creation fails, capture the error code via PTR_ERR(), clear the
dangling pointer tracking entry to NULL, and jump to a newly added
'err_poweroff' label to unwind hardware settings and clear out
allocated structural components cleanly.
2. In vdec_stop_streaming(), protect the teardown execution track by
wrapping the kthread_stop() invocation inside a safe !IS_ERR_OR_NULL()
conditional block to ensure the target thread structure actually exists
in system memory before stopping it.
3. In vdec_close(), cleanly wind down and terminate any lingering
recycling threads at the absolute entry boundary of the routine
before flushing data framework contexts and releasing the file
descriptor matrices to user space.
Together, these changes ensure predictable driver behavior, prevent
kernel panics during low-memory conditions, and guarantee that memory
and DMA assets are fully reclaimed during teardown.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 48 +++++++++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d1f35fc893de1..7fc73d5cdebbf 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -32,6 +32,10 @@ struct dummy_buf {
/* 16 MiB for parsed bitstream swap exchange */
#define SIZE_VIFIFO SZ_16M
+static void vdec_free_canvas(struct amvdec_session *sess);
+static void vdec_reset_timestamps(struct amvdec_session *sess);
+static void vdec_reset_bufs_recycle(struct amvdec_session *sess);
+
static u32 get_output_size(u32 width, u32 height)
{
return ALIGN(width * height, SZ_64K);
@@ -353,13 +357,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->sequence_cap = 0;
sess->sequence_out = 0;
- if (vdec_codec_needs_recycle(sess))
+ if (vdec_codec_needs_recycle(sess) && !sess->recycle_thread) {
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
-
+ if (IS_ERR(sess->recycle_thread)) {
+ ret = PTR_ERR(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ goto err_poweroff;
+ }
+ }
schedule_work(&sess->esparser_queue_work);
return 0;
+err_poweroff:
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
err_free_vififo:
if (sess->vififo_vaddr) {
dma_free_coherent(sess->core->dev, sess->vififo_size,
@@ -464,7 +478,10 @@ static void vdec_stop_streaming(struct vb2_queue *q)
if (full_cleanup) {
if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
!sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
- kthread_stop(sess->recycle_thread);
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
}
vdec_poweroff(sess);
@@ -970,6 +987,31 @@ static int vdec_open(struct file *file)
static int vdec_close(struct file *file)
{
struct amvdec_session *sess = file_to_amvdec_session(file);
+ struct amvdec_core *core = sess->core;
+
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
+
+ mutex_lock(&core->lock);
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ core->cur_sess = NULL;
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ kfree(sess->priv);
+ sess->priv = NULL;
+
+ mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
v4l2_fh_del(&sess->fh, file);
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (2 preceding siblings ...)
2026-07-13 12:06 ` [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread Anand Moon
@ 2026-07-13 12:06 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ Anand Moon
` (14 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Prevent critical race conditions and null pointer dereferences inside
the Amlogic video decoder driver by synchronizing the interrupt service
routines during session teardown and file descriptor release phases.
The hardware interrupt handler (vdec_isr) and threaded handler
(vdec_threaded_isr) read 'core->cur_sess' without proper synchronization.
During a streaming teardown sequence via vdec_stop_streaming() or session
release inside vdec_close(), the underlying session structures can be
modified or freed concurrently. This creates a transient window where a
late-stage hardware interrupt can wake up, read 'core->cur_sess', and
dereference an invalid or NULL session pointer, causing a kernel panic.
Update both the hard and threaded interrupt service routines to capture
the current session reference using an smp_load_acquire() barrier snapshot.
If the snapshot resolves to NULL, terminate immediately with IRQ_HANDLED
to guarantee execution contexts do not process stale structures.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Suggested-by: Druk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 22 ++++++++++++++++++++--
drivers/staging/media/meson/vdec/vdec.h | 2 ++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7fc73d5cdebbf..7ae3d5a9dd6ab 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,6 +484,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -989,6 +992,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
@@ -1038,7 +1044,12 @@ static const struct v4l2_file_operations vdec_fops = {
static irqreturn_t vdec_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic acquire snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
sess->last_irq_jiffies = get_jiffies_64();
@@ -1048,7 +1059,12 @@ static irqreturn_t vdec_isr(int irq, void *data)
static irqreturn_t vdec_threaded_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Prevent late-stage threaded interrupts from dereferencing a NULL session */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
return sess->fmt_out->codec_ops->threaded_isr(sess);
}
@@ -1136,6 +1152,8 @@ static int vdec_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ core->vdec_irq = irq;
+
ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
vdec_threaded_isr, IRQF_ONESHOT,
"vdec", core);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index cc0cfafb8a951..d165c343fd022 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -67,6 +67,7 @@ struct amvdec_session;
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
+ * @vdec_irq: irq for video decoding
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -93,6 +94,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
+ int vdec_irq;
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (3 preceding siblings ...)
2026-07-13 12:06 ` [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync Anand Moon
` (13 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko
During session teardown sequences in vdec_stop_streaming() and
vdec_close(), the 'esparser' hardware interrupt handler can still be
actively triggered or executing on another CPU core. This creates a
transient race condition where the ISR attempts to handle stream data and
allocate internal tracking state structures after session contexts have
been modified or freed.
Update esparser_isr() to read the current session context utilizing an
smp_load_acquire() barrier snapshot. If the pointer resolves to NULL,
terminate processing early with IRQ_HANDLED to protect against
concurrent dismantling.
Suggested-by: Doruk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 8 ++++++++
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.h | 2 ++
3 files changed, 14 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 4632346f04a9e..37749ede308c6 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -60,6 +60,12 @@ static irqreturn_t esparser_isr(int irq, void *dev)
{
int int_status;
struct amvdec_core *core = dev;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
int_status = amvdec_read_parser(core, PARSER_INT_STATUS);
amvdec_write_parser(core, PARSER_INT_STATUS, int_status);
@@ -439,6 +445,8 @@ int esparser_init(struct platform_device *pdev, struct amvdec_core *core)
if (irq < 0)
return irq;
+ core->esparser_irq = irq;
+
ret = devm_request_irq(dev, irq, esparser_isr, IRQF_SHARED,
"esparserirq", core);
if (ret) {
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7ae3d5a9dd6ab..7689ffdb2e500 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -486,6 +486,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -994,6 +996,8 @@ static int vdec_close(struct file *file)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index d165c343fd022..c4639cf33e73e 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -68,6 +68,7 @@ struct amvdec_session;
* @cur_sess: current decoding session
* @lock: video device lock
* @vdec_irq: irq for video decoding
+ * @esparser_irq: irq for elementary stream parsing
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -95,6 +96,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
int vdec_irq;
+ int esparser_irq;
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (4 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race Anand Moon
` (12 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Synchronize work-queue states cleanly during streaming teardown and
close operations to eliminate asynchronous pipeline race conditions.
The esparser ISR can schedule work onto 'esparser_queue_work'. While
synchronize_irq() ensures the ISR itself finishes executing, it does
not prevent any already scheduled work items from running concurrently
during driver teardown. This causes a race condition during stream
stopping or file closing.
Remove the scheduling of esparser queue work during destination buffer
completion, as freeing the vififo is handled elsewhere. Add synchronous
cancellation of any pending work in vdec_stop_streaming and vdec_close
before synchronizing interrupts to prevent use-after-free and race
conditions.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7689ffdb2e500..6fe9722577179 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,11 +484,15 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ mutex_unlock(&core->lock);
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+ mutex_lock(&core->lock);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -999,6 +1003,8 @@ static int vdec_close(struct file *file)
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (5 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown Anand Moon
` (11 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Rework the esparser work queue logic to prevent concurrent queue mutations
and fix a race condition during streaming teardown.
The previous implementation relied on v4l2_m2m_for_each_src_buf_safe(),
which held the session mutex across the entire queue traversal. This
blocked concurrent operations, caused inaccurate buffer mapping, and
triggered 100+ second watchdog freezes due to an unyielding polling loop.
Additionally, the low-level parser handled buffer ownership adjustments
and completion callbacks directly, leading to inconsistent error paths and
races between streamon and streamoff.
Fix this by utilizing the modern scoped_guard(mutex) mechanism inside the
main worker loop. This allows the session lock to be automatically
dropped on any conditional breakout path, preventing voluntary scheduling
bugs while invoking cond_resched(). De-couple buffer lifecycles from the
hardware parser routine by leveraging safe, linear fetches via
v4l2_m2m_next_src_buf() and v4l2_m2m_src_buf_remove(), ensuring orderly
frame transactions. Refactor esparser_queue() to return explicit error
codes (such as -EBADMSG, -EIO, and -EAGAIN) to safely handle buffer states.
Clean up teardown paths: Execute cancel_work_sync() inside
vdec_stop_streaming() and vdec_close() immediately after
synchronize_irq(). This ensures any pending scheduled queue work is fully
flushed and canceled before the driver destroys hardware instances.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 69 +++++++++++++++------
1 file changed, 49 insertions(+), 20 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 37749ede308c6..e5d3d817b9b2b 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -328,15 +328,11 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
return -EAGAIN;
}
- v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);
-
offset = esparser_get_offset(sess);
ret = amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags);
- if (ret) {
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ if (ret)
return ret;
- }
dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n",
vb->timestamp, payload_size, offset, vbuf->flags);
@@ -348,12 +344,10 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
payload_size = vp9_update_header(core, vb);
- /* If unable to alter buffer to add headers */
if (payload_size == 0) {
+ dev_err(core->dev, "esparser: VP9 header update failed\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
-
- return 0;
+ return -EBADMSG;
}
}
@@ -363,33 +357,68 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
-
- return 0;
+ return -EIO;
}
atomic_inc(&sess->esparser_queued_bufs);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
return 0;
}
void esparser_queue_all_src(struct work_struct *work)
{
- struct v4l2_m2m_buffer *buf, *n;
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
+ struct device *dev = sess->core->dev_dec;
+ int ret;
- mutex_lock(&sess->lock);
- v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
- if (sess->should_stop)
- break;
+ while (1) {
+ struct vb2_v4l2_buffer *vbuf = NULL;
+ bool processed_frame = false;
+
+ scoped_guard(mutex, &sess->lock) {
+ /* Safe atomic tracking check: exit loop if session is shutting down */
+ if (sess->should_stop)
+ return;
+
+ /* Queue completely empty: exit work loop cleanly */
+ vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
+ if (!vbuf)
+ break;
+
+ /* Stop processing if we hit the end-of-stream drain buffer */
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST)
+ break;
+
+ /* Check hardware FIFO limits safely inside the locks */
+ ret = esparser_queue(sess, vbuf);
+ if (ret == -EAGAIN)
+ break;
+
+ /* Pop the buffer from the source queue since it is now processed */
+ vbuf = v4l2_m2m_src_buf_remove(sess->m2m_ctx);
+ if (!vbuf) {
+ dev_dbg(dev, "Buffer missing during queue removal\n");
+ break;
+ }
+
+ /* Complete the buffer transaction based on parser results */
+ if (ret < 0)
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ else
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
- if (esparser_queue(sess, &buf->vb) < 0)
+ /* Set tracking flag indicating transaction completion */
+ processed_frame = true;
+ }
+
+ if (processed_frame)
break;
+
+ /* Give other threads and IRQ routines a window to execute while unlocked */
+ cond_resched();
}
- mutex_unlock(&sess->lock);
}
int esparser_power_up(struct amvdec_session *sess)
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (6 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads Anand Moon
` (10 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
The esparser work queue reads 'sess->should_stop' outside of critical
regions without serialization primitives, risking data races or visibility
delays. Furthermore, vdec_close() and vdec_stop_streaming() blindly
shut down hardware components (via poweroff and canvas frees) and nullify
'core->cur_sess' without confirming that the executing session actually
owns the active hardware context. In multi-session scenarios, this allows
a closing inactive session to inadvertently break a running session.
To fix these synchronization and lifecycle issues with the following
changes use thread-safe flagging: Wrap reads and writes of
'sess->should_stop' in READ_ONCE() and WRITE_ONCE() to prevent compiler
optimizations from caching the condition variables across scheduling
boundaries and optimizations across workqueue execution threads.
Also safe context releasing: transition 'core->cur_sess' pointer
clearings to use smp_store_release(). This ensures all prior internal
memory structures are entirely flushed and visible to other execution
cores.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 2 +-
drivers/staging/media/meson/vdec/vdec.c | 88 ++++++++++++++-------
2 files changed, 59 insertions(+), 31 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index e5d3d817b9b2b..959673742e699 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
- if (sess->should_stop)
+ if (READ_ONCE(sess->should_stop))
return;
/* Queue completely empty: exit work loop cleanly */
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6fe9722577179..83a9b1238972a 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -287,9 +287,13 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct amvdec_session *sess = vb2_get_drv_priv(q);
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
+ struct device *dev = core->dev_dec;
struct vb2_v4l2_buffer *buf;
int ret;
+ /* Reset workqueue loop shutdown signal to allow streaming */
+ WRITE_ONCE(sess->should_stop, 0);
+
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -336,7 +340,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
dma_alloc_coherent(sess->core->dev, sess->vififo_size,
&sess->vififo_paddr, GFP_KERNEL);
if (!sess->vififo_vaddr) {
- dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
+ dev_err(dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
goto err_cleanup_session;
}
@@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->streamon_cap = 0;
mutex_lock(&core->lock);
- if (core->cur_sess == sess)
- core->cur_sess = NULL;
- if (sess->status != STATUS_NEEDS_RESUME)
- sess->status = STATUS_STOPPED;
+ if (core->cur_sess == sess) {
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ }
mutex_unlock(&core->lock);
err_unlock_no_hw:
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -441,6 +447,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
enum amvdec_status old_status;
bool full_cleanup = false;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
- if (sess->vififo_vaddr) {
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
- }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- core->cur_sess = NULL;
+ kfree(sess->priv);
+ sess->priv = NULL;
- kfree(sess->priv);
- sess->priv = NULL;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ }
} else {
if (sess->status == STATUS_NEEDS_RESUME)
sess->changed_format = 0;
}
+
mutex_unlock(&core->lock);
}
@@ -802,7 +818,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
if (cmd->cmd == V4L2_DEC_CMD_START) {
v4l2_m2m_clear_state(sess->m2m_ctx);
- sess->should_stop = 0;
+ WRITE_ONCE(sess->should_stop, 0);
return 0;
}
@@ -812,7 +828,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n");
- sess->should_stop = 1;
+ WRITE_ONCE(sess->should_stop, 1);
v4l2_m2m_mark_stopped(sess->m2m_ctx);
@@ -998,6 +1014,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
@@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- core->cur_sess = NULL;
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
- if (sess->vififo_vaddr) {
- dma_free_coherent(core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
}
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
+
kfree(sess->priv);
sess->priv = NULL;
+ /* Unconditionally set our local status to stopped */
+ sess->status = STATUS_STOPPED;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+
mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (7 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline Anand Moon
` (9 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.
When v4l2-compliance injects short, empty, or uninitialized test buffers,
the validation logic catches the out-of-bounds size anomalies, but
returning 0 tricks the calling esparser infrastructure into treating
it as a successful 0-byte header conversion. This causes the hardware
decoder engine to stall out, resulting in subsequent stream-on timeouts
and failure marks inside v4l2-test-buffers.cpp.
Fix this by returning -EINVAL across all validation and bounds check
failures to force immediate core framework buffer drops. Additionally,
tighten array pointer checks, secure the mag_ptr bounds loop, and
convert the superframe parsing indexer into an explicit if/else block
for readability.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 959673742e699..edbfc829e2da8 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
unsigned char *old_header = NULL;
dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
+ if (!dp)
+ return -EINVAL;
+
dsize = vb2_get_plane_payload(buf, 0);
- if (dsize == vb2_plane_size(buf, 0)) {
- dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
+ dev_warn(core->dev, "%s: invalid payload size %d\n",
+ __func__, dsize);
+ return -EINVAL;
}
marker = dp[dsize - 1];
@@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
num_frames = (marker & 0x7) + 1;
mag = ((marker >> 3) & 0x3) + 1;
mag_ptr = dsize - mag * num_frames - 2;
- if (dp[mag_ptr] != marker)
- return 0;
+ if (mag_ptr < 0 || dp[mag_ptr] != marker)
+ return -EINVAL;
mag_ptr++;
for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
frame_size[cur_frame] = 0;
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
+ if (mag_ptr >= dsize)
+ return -EINVAL;
+
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
@@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
if (new_frame_size >= vb2_plane_size(buf, 0)) {
dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ return -ENOMEM;
}
for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) {
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (8 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions Anand Moon
` (8 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Fix a circular locking dependency (lockdep warning) and a potential KASAN
wild-memory-access race condition within the asynchronous esparser queue
by restructuring register write and context evaluation blocks.
Previously, evaluating hardware ownership checks outside of core->lock
introduced a time-of-check to time-of-use vulnerability, risking register
corruption during parallel stream initialization cycles. Additionally,
abruptly exiting the payload queue loop without sanitizing timestamp
trackers left metadata dynamically stranded in memory, triggering regular
kmemleak warnings.
Fix these flaws by moving the hardware session ownership validation and
atomic stop signal checks directly inside the core->lock mutex region in
esparser_queue(). Ensure that presentation timestamp pointers are cleanly
removed via amvdec_remove_ts() on all early-exit routes. Finally, refine
the VP9 payload evaluation logic to correctly catch negative error passes
from vp9_update_header() and pass the session context explicitly to the
padding helper to secure memory operations.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 29 ++++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index edbfc829e2da8..b9f36fef4be12 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -199,12 +199,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
* the ESPARSER interrupt.
*/
static u32 esparser_pad_start_code(struct amvdec_core *core,
+ struct amvdec_session *sess,
struct vb2_buffer *vb,
u32 payload_size)
{
u32 pad_size = 0;
u8 *vaddr = vb2_plane_vaddr(vb, 0);
+ if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
+ return 0;
+
if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr + payload_size, 0, pad_size);
@@ -313,6 +317,9 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
u32 offset;
u32 pad_size;
+ if (READ_ONCE(sess->should_stop) || !sess->priv)
+ return -ESHUTDOWN;
+
/*
* When max ref frame is held by VP9, this should be -= 3 to prevent a
* shortage of CAPTURE buffers on the decoder side.
@@ -349,24 +356,38 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
vbuf->sequence = sess->sequence_out++;
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
- payload_size = vp9_update_header(core, vb);
+ int res = vp9_update_header(core, vb);
- if (payload_size == 0) {
- dev_err(core->dev, "esparser: VP9 header update failed\n");
+ if (res <= 0) {
+ dev_err(core->dev,
+ "esparser: VP9 header update failed (%d)\n",
+ res);
amvdec_remove_ts(sess, vb->timestamp);
return -EBADMSG;
}
+ payload_size = res;
+ }
+
+ pad_size = esparser_pad_start_code(core, sess, vb, payload_size);
+
+ /* Protect hardware register writes under core->lock */
+ mutex_lock(&core->lock);
+ if (core->cur_sess != sess || READ_ONCE(sess->should_stop)) {
+ mutex_unlock(&core->lock);
+ amvdec_remove_ts(sess, vb->timestamp);
+ return -ESHUTDOWN;
}
- pad_size = esparser_pad_start_code(core, vb, payload_size);
ret = esparser_write_data(core, phy, payload_size + pad_size);
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
+ mutex_unlock(&core->lock);
return -EIO;
}
+ mutex_unlock(&core->lock);
atomic_inc(&sess->esparser_queued_bufs);
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (9 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop Anand Moon
` (7 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Explicitly update the V4L2 core Memory-to-Memory (m2m) framework's
internal streaming states when initiating or terminating streaming on
the device queues.
Without invoking these core framework helpers, the m2m engine's
bookkeeping of active source and destination queues drifts out of
alignment with the driver's local 'streamon_out' and 'streamon_cap'
tracking variables. This misalignment causes state validation stalls
and incorrect polling outcomes when the device is subjected to quick
runtime cycles or strict testing setups (such as v4l2-compliance).
Fix this by integrating v4l2_m2m_update_start_streaming_state() right
after a queue is marked active in vdec_start_streaming(), and pairing
it cleanly with v4l2_m2m_update_stop_streaming_state() at the end of
vdec_stop_streaming() before the session core mutex is released.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 83a9b1238972a..0eb39aa6014ee 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -299,6 +299,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
else
sess->streamon_cap = 1;
+ v4l2_m2m_update_start_streaming_state(sess->m2m_ctx, q);
+
if (!sess->streamon_out)
return 0;
@@ -529,6 +531,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
sess->changed_format = 0;
}
+ v4l2_m2m_update_stop_streaming_state(sess->m2m_ctx, q);
+
mutex_unlock(&core->lock);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (10 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence Anand Moon
` (6 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Introduce proper V4L2 Memory-to-Memory (m2m) scheduler pipeline tracking
by handling job finalisation inside the asynchronous esparser workqueue.
Because the meson video decoder offloads raw hardware register processing
and bitstream feeding to an internal workqueue engine, calling the helper
v4l2_m2m_job_finish() prematurely within the primary device execution
trigger context (vdec_m2m_device_run) drops the active task transaction
state too early. This timing gap creates a state mismatch in user-space
multimedia layers like GStreamer, resulting in a fatal "poll error 1"
event abort that breaks streaming setup sequences during pipeline preroll.
Resolve this architectural loop collision by deferring the scheduling call
to v4l2_m2m_job_finish() to execute exclusively within the worker routine
esparser_queue_all_src() at the precise microsecond after an input buffer
payload is fully validated, cleared, and returned via v4l2_m2m_buf_done().
Additionally, protect hardware session context mappings with core mutex
locks and implement volatile early exit gates within vdec_m2m_device_run()
to ensure stable teardowns.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
2 files changed, 15 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index b9f36fef4be12..939b239c2af47 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -399,6 +399,7 @@ void esparser_queue_all_src(struct work_struct *work)
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
struct device *dev = sess->core->dev_dec;
+ struct amvdec_core *core = sess->core;
int ret;
while (1) {
@@ -437,6 +438,9 @@ void esparser_queue_all_src(struct work_struct *work)
else
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ /* Safely notify the V4L2 core sub-framework */
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+
/* Set tracking flag indicating transaction completion */
processed_frame = true;
}
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 0eb39aa6014ee..b3e1d99e8889f 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -149,6 +149,17 @@ vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb)
static void vdec_m2m_device_run(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ if (READ_ONCE(sess->should_stop)) {
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+ return;
+ }
+
+ mutex_lock(&core->lock);
+ if (!core->cur_sess)
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
schedule_work(&sess->esparser_queue_work);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (11 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done Anand Moon
` (5 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Implement proper cleanup inside vdec_m2m_job_abort to safely stop
the hardware job sequence during a streaming abort or teardown.
Without this, if a job is aborted right after being triggered, the
deferred work item (esparser_queue_work) scheduled by device_run
could continue running concurrently. This leads to unexpected behavior
and potential use-after-free bugs if session structures are cleared.
Fix this by flagging the session to stop via WRITE_ONCE, synchronously
canceling any pending parser work, and safely clearing the active core
session pointer under the core lock protection.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index b3e1d99e8889f..ac86a9c4febff 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -167,6 +167,17 @@ static void vdec_m2m_device_run(void *priv)
static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ WRITE_ONCE(sess->should_stop, 1);
+
+ cancel_work_sync(&sess->esparser_queue_work);
+
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ mutex_unlock(&core->lock);
v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (12 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout Anand Moon
` (4 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Fix an end-of-stream (EOS) boundary condition logic flaw in
dst_buf_done() by relocating the atomic decrement helper to execute
after state evaluation.
Previously, decrementing sess->esparser_queued_bufs at the entry point
of the function corrupted the conditional checks for the final video
frame. This premature decrement tricked the driver into flagging the
second-to-last buffer as the final frame (V4L2_BUF_FLAG_LAST) during
teardown sequences, which cut off the true final frame and broke
compliance validation.
Resolve this by moving atomic_dec() to execute immediately after the EOS
conditional tracking block finishes evaluating, right before invoking
v4l2_m2m_buf_done(). This ensures accurate stream termination
signaling.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_helpers.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
index f02c21d5a9c18..303236f0647c0 100644
--- a/drivers/staging/media/meson/vdec/vdec_helpers.c
+++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
@@ -314,6 +314,9 @@ static void dst_buf_done(struct amvdec_session *sess,
dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
vbuf->vb2_buf.index, timestamp, flags);
vbuf->field = field;
+
+ atomic_dec(&sess->esparser_queued_bufs);
+
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
/* Buffer done probably means the vififo got freed */
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (13 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe Anand Moon
` (3 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
The vdec_1 firmware loader used static variables for DMA buffer
addresses (`mc_addr`, `mc_addr_map`). This made allocations global
across sessions, corrupting state when multiple decoders loaded
firmware concurrently. Under stress this triggered KASAN reports,
lost references, and memory leaks. Making mc_addr and mc_addr_map
local to each load call, eliminating cross‑session races.
The firmware DMA completion check also relied on a raw decrement
loop of 1000 iterations. On modern CPUs this loop completed far
too quickly, often before hardware signaled ready, producing
false "DMA hang" errors. Replacing the busy‑wait loop with
readl_poll_timeout_atomic(), providing a bounded, microsecond
scale poll with proper timeout semantics.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_1.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c
index a65cb49594465..27363c9a5af3c 100644
--- a/drivers/staging/media/meson/vdec/vdec_1.c
+++ b/drivers/staging/media/meson/vdec/vdec_1.c
@@ -29,14 +29,14 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
struct amvdec_core *core = sess->core;
struct device *dev = core->dev_dec;
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
- static void *mc_addr;
- static dma_addr_t mc_addr_map;
+ void *mc_addr;
+ dma_addr_t mc_addr_map;
int ret;
- u32 i = 1000;
+ u32 val;
ret = request_firmware(&fw, fwname, dev);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (fw->size < MC_SIZE) {
dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
@@ -63,11 +63,11 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4);
amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
- while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000);
-
- if (i == 0) {
+ ret = readl_poll_timeout_atomic(core->dos_base + IMEM_DMA_CTRL, val,
+ !(val & 0x8000), 10, 10000);
+ if (ret) {
dev_err(dev, "Firmware load fail (DMA hang?)\n");
- ret = -EINVAL;
+ ret = -ETIMEDOUT;
goto free_mc;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (14 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device Anand Moon
` (2 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
The vdec probe routine does not set explicit DMA constraints, leaving the
driver completely dependent on platform bus default values. This lack of
explicit layout initialization can lead to allocation failures, restricted
address space mappings, or broken contiguous buffer handling on
architectures with restrictive DMA layers.
Address these platform constraints with the following changes during driver
initialization:
1. Enforce a 32-bit coherent DMA allocation window by invoking
dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous allocation segment boundary constraint to
UINT_MAX using the vb2_dma_contig_set_max_seg_size() configuration
helper.
This guarantees that large, contiguous video frame allocation requests
work reliably and explicitly aligns the memory management paths with
standard Linux kernel DMA management paradigms.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index ac86a9c4febff..d33cbebc4453b 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -1186,6 +1186,16 @@ static int vdec_probe(struct platform_device *pdev)
if (IS_ERR(core->canvas))
return PTR_ERR(core->canvas);
+ /* Enforce strict 32-bit DMA limit to match hardware capabilities */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set 32-bit DMA mask\n");
+
+ ret = vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set DMA max segment size\n");
+
of_id = of_match_node(vdec_dt_match, dev->of_node);
core->platform = of_id->data;
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (15 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB Anand Moon
2026-07-13 12:07 ` [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries Anand Moon
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
The vdec driver retrieves a canvas instance using meson_canvas_get() and
stores the raw pointer in core->canvas. However, the helper drops the
provider device reference via put_device() right before returning. Because
no formal device relationship is established, an unbind of the canvas
provider driver triggers a Use-After-Free (UAF) bug when the video decoder
subsequently attempts to access that memory block.
Fix this lifecycle hazard by enhancing meson_canvas_get() to establish a
formal managed device link between the consumer device and the underlying
canvas platform device. Using DL_FLAG_AUTOREMOVE_CONSUMER ensures the
dependency link is automatically torn down when the consumer driver
unbinds.
Additionally, handle missing link conditions using dev_err_probe() to
clean up upstream error diagnostics, and ensure the canvas platform device
reference count is dropped safely along the uninitialized driver data
path.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530111022.9C6D71F00893@smtp.kernel.org
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/soc/amlogic/meson-canvas.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
index 79681afea8c61..5102a491bf778 100644
--- a/drivers/soc/amlogic/meson-canvas.c
+++ b/drivers/soc/amlogic/meson-canvas.c
@@ -54,6 +54,7 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
struct device_node *canvas_node;
struct platform_device *canvas_pdev;
struct meson_canvas *canvas;
+ struct device_link *link;
canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
if (!canvas_node)
@@ -70,9 +71,18 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
* current state, this driver probe cannot return -EPROBE_DEFER
*/
canvas = dev_get_drvdata(&canvas_pdev->dev);
- put_device(&canvas_pdev->dev);
- if (!canvas)
+ if (!canvas) {
+ put_device(&canvas_pdev->dev);
return ERR_PTR(-EINVAL);
+ }
+
+ /* Establish device link to prevent Use-After-Free */
+ link = device_link_add(dev, &canvas_pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ put_device(&canvas_pdev->dev);
+ if (!link)
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "Failed to create device link canvas\n"));
return canvas;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (16 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries Anand Moon
18 siblings, 0 replies; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
During 4K video playback, the hardware Elementary Stream Parser (esparser)
can rapidly push massive burst data payloads into the Video Input FIFO
faster than the VPU decoding block can consume it. This causes a buffer
overflow when stream usage breaks past the allocated hardware boundary:
[ 1852.587956] meson-vdec ff620000.video-decoder: VIFIFO usage (16779003) > VIFIFO size (16777216)
When this overflow happens on Amlogic SoCs, it triggers an invalid memory
state, leading to stream corruption and fatal kernel panic lockups within
videobuf2 error rollback handling routines.
Double the parsed bitstream swap exchange memory pool (SIZE_VIFIFO) from
16 MiB (SZ_16M) to 32 MiB (SZ_32M). This provides a larger canvas window
to cushion high-frequency frame reordering spikes and prevents the VPU
from crashing on demanding modern streams.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d33cbebc4453b..824e2f156adeb 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -29,8 +29,8 @@ struct dummy_buf {
struct list_head list;
};
-/* 16 MiB for parsed bitstream swap exchange */
-#define SIZE_VIFIFO SZ_16M
+/* 32 MiB for parsed bitstream swap exchange */
+#define SIZE_VIFIFO SZ_32M
static void vdec_free_canvas(struct amvdec_session *sess);
static void vdec_reset_timestamps(struct amvdec_session *sess);
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
` (17 preceding siblings ...)
2026-07-13 12:07 ` [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB Anand Moon
@ 2026-07-13 12:07 ` Anand Moon
2026-07-13 14:04 ` Nicolas Dufresne
18 siblings, 1 reply; 21+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
When importing DMABUFs exported by the Amlogic video decoder driver
(meson_vdec) for hardware-accelerated rendering paths, the DMA core
subsystem throws constraint validation warnings. This occurs because the
display controller master device lacks explicit DMA layout configuration,
causing it to fall back to a default 64KB maximum segment size limit.
Address these architectural constraints during the master bind sequence:
1. Initialize and validate a 32-bit coherent DMA allocation window by
invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous scatter-gather allocation segment boundary
check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
This guarantees that large video bitstream frame buffers can be imported
and scanned out across sub-driver domains without triggering allocation
warnings or page boundary splits.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 49ff9f1f16d32..899e70bca4ce2 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (IS_ERR(drm))
return PTR_ERR(drm);
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto free_drm;
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
--
2.50.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
2026-07-13 12:07 ` [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries Anand Moon
@ 2026-07-13 14:04 ` Nicolas Dufresne
0 siblings, 0 replies; 21+ messages in thread
From: Nicolas Dufresne @ 2026-07-13 14:04 UTC (permalink / raw)
To: Anand Moon, Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
[-- Attachment #1: Type: text/plain, Size: 1889 bytes --]
Le lundi 13 juillet 2026 à 17:37 +0530, Anand Moon a écrit :
> When importing DMABUFs exported by the Amlogic video decoder driver
> (meson_vdec) for hardware-accelerated rendering paths, the DMA core
> subsystem throws constraint validation warnings. This occurs because the
> display controller master device lacks explicit DMA layout configuration,
> causing it to fall back to a default 64KB maximum segment size limit.
>
> Address these architectural constraints during the master bind sequence:
>
> 1. Initialize and validate a 32-bit coherent DMA allocation window by
> invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
> 2. Maximize the contiguous scatter-gather allocation segment boundary
> check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
>
> This guarantees that large video bitstream frame buffers can be imported
> and scanned out across sub-driver domains without triggering allocation
> warnings or page boundary splits.
>
> Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
> Signed-off-by: Anand Moon <linux.amoon@gmail.com>
Acked-by: Nicolas Dufresne <nicolas@ndufresne.ca>
> ---
> drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 49ff9f1f16d32..899e70bca4ce2 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
> if (IS_ERR(drm))
> return PTR_ERR(drm);
>
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + goto free_drm;
> +
> + dma_set_max_seg_size(dev, UINT_MAX);
> +
> priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> if (!priv) {
> ret = -ENOMEM;
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread