[PATCH] media: ti: vpe: quiesce overflow recovery before freeing streams
From: Fan Wu
Date: Mon Jul 06 2026 - 21:54:25 EST
The VIP overflow recovery work is armed from the hardirq handler when a
FIFO overflow is detected, and the list-complete path looks the stream
up through the VPDMA list private pointer. Both keep touching stream,
port and device state; the recovery worker also resets the parser and
VPDMA and can re-enable overflow interrupts.
vip_stop_streaming() masks and clears the per-list IRQs, but it neither
synchronizes the hardirq handler nor cancels recovery_work. If an
overflow IRQ has already queued recovery_work, or a list-complete IRQ is
in flight when the stream is torn down, the handler or worker can still
dereference the stream after its resources are released.
free_stream() owns the stream lifetime, so drain the IRQ handler and
recovery work there before freeing stream-owned resources: drop the
stream from cap_streams[], disable IRQs for its list (disable_irqs()
masks both the parser-overflow and the list-complete IRQ), wait for any
in-flight handler, cancel the worker, then disable and sync again
because the worker may have re-enabled interrupts while it ran. Only
then are the drop queue, video device and VPDMA list released and the
stream freed.
Additionally clear the VPDMA list private pointer in vpdma_hwlist_release
(and return the released slot's value instead of the array base), so
later list-complete handling cannot recover a freed stream through a
stale private pointer.
Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Fan Wu <fanwu01@xxxxxxxxxx>
---
drivers/media/platform/ti/vpe/vip.c | 20 +++++++++++++++++++-
drivers/media/platform/ti/vpe/vpdma.c | 3 ++-
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
index cb0a5a07a3d4..9c5bf91ade1b 100644
--- a/drivers/media/platform/ti/vpe/vip.c
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -3139,6 +3139,25 @@ static void free_stream(struct vip_stream *stream)
return;
dev = stream->port->dev;
+ /*
+ * Quiesce the overflow IRQ and recovery work for this stream
+ * before releasing its resources: the handler and the worker
+ * both keep touching stream, port and device state. disable_irqs()
+ * masks both the parser-overflow and the list-complete IRQ for
+ * this list. Drop the stream from cap_streams[] first so a racing
+ * overflow handler misses the lookup, wait for any in-flight
+ * handler, cancel the worker, then disable and sync again because
+ * the worker may have re-enabled interrupts while it ran.
+ */
+ stream->port->cap_streams[stream->stream_id] = NULL;
+ disable_irqs(dev, dev->slice_id, stream->list_num);
+ clear_irqs(dev, dev->slice_id, stream->list_num);
+ synchronize_irq(dev->irq);
+ cancel_work_sync(&stream->recovery_work);
+ disable_irqs(dev, dev->slice_id, stream->list_num);
+ clear_irqs(dev, dev->slice_id, stream->list_num);
+ synchronize_irq(dev->irq);
+
/* Free up the Drop queue */
list_for_each_safe(pos, q, &stream->dropq) {
buf = list_entry(pos,
@@ -3150,7 +3169,6 @@ static void free_stream(struct vip_stream *stream)
video_unregister_device(stream->vfd);
vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
- stream->port->cap_streams[stream->stream_id] = NULL;
kfree(stream);
}
diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c
index 573aa83f62eb..f9f5b2f1ee1a 100644
--- a/drivers/media/platform/ti/vpe/vpdma.c
+++ b/drivers/media/platform/ti/vpe/vpdma.c
@@ -988,7 +988,8 @@ void *vpdma_hwlist_release(struct vpdma_data *vpdma, int list_num)
spin_lock_irqsave(&vpdma->lock, flags);
vpdma->hwlist_used[list_num] = false;
- priv = vpdma->hwlist_priv;
+ priv = vpdma->hwlist_priv[list_num];
+ vpdma->hwlist_priv[list_num] = NULL;
spin_unlock_irqrestore(&vpdma->lock, flags);
return priv;
--
2.34.1