On Wed Mar 4 16:50:24 2026 +0100, Stefan Klug wrote:
> On kernels with PREEMPT_RT enabled, a "BUG: scheduling while atomic"
> kernel oops occurs inside dw100_irq_handler -> vb2_buffer_done. This is
> because vb2_buffer_done takes a spinlock which is not allowed within
> interrupt context on PREEMPT_RT.
> 
> The first attempt to fix this was to just drop the IRQF_ONESHOT so that
> the interrupt is handled threaded on PREEMPT_RT systems. This introduced
> a new issue. The dw100 has an internal timeout counter that is gated by
> the DW100_BUS_CTRL_AXI_MASTER_ENABLE bit. Depending on the time it takes
> for the threaded handler to run and the geometry of the data being
> processed it is possible to reach the timeout resulting in
> DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT being set and "dw100
> 32e30000.dwe: Interrupt error: 0x1" errors in dmesg.
> 
> To properly fix that, split the interrupt into two halves, reset the
> DW100_BUS_CTRL_AXI_MASTER_ENABLE bit in the hard interrupt handler and
> do the v4l2 buffer handling in the threaded half. The IRQF_ONESHOT can
> still be dropped as the interrupt gets disabled in the hard handler and
> will only be reenabled on the next dw100_device_run which will not be
> called before the current job has finished.
> 
> Signed-off-by: Stefan Klug <[email protected]>
> Reviewed-by: Xavier Roumegue <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Link: 
> https://patch.msgid.link/20260304-sklug-v6-16-topic-dw100-v3-1-dev-v5-3-1a7e1f721...@ideasonboard.com
> Signed-off-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/platform/nxp/dw100/dw100.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

---

diff --git a/drivers/media/platform/nxp/dw100/dw100.c 
b/drivers/media/platform/nxp/dw100/dw100.c
index a0a9a3f0dc1e..42748d0f0483 100644
--- a/drivers/media/platform/nxp/dw100/dw100.c
+++ b/drivers/media/platform/nxp/dw100/dw100.c
@@ -11,6 +11,7 @@
 #include <linux/debugfs.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/irqreturn.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -74,6 +75,7 @@ struct dw100_device {
        struct clk_bulk_data            *clks;
        int                             num_clks;
        struct dentry                   *debugfs_root;
+       bool                            frame_failed;
 };
 
 struct dw100_q_data {
@@ -1404,7 +1406,8 @@ static irqreturn_t dw100_irq_handler(int irq, void 
*dev_id)
 {
        struct dw100_device *dw_dev = dev_id;
        u32 pending_irqs, err_irqs, frame_done_irq;
-       bool with_error = true;
+
+       dw_dev->frame_failed = true;
 
        pending_irqs = dw_hw_get_pending_irqs(dw_dev);
        frame_done_irq = pending_irqs & DW100_INTERRUPT_STATUS_INT_FRAME_DONE;
@@ -1412,7 +1415,7 @@ static irqreturn_t dw100_irq_handler(int irq, void 
*dev_id)
 
        if (frame_done_irq) {
                dev_dbg(&dw_dev->pdev->dev, "Frame done interrupt\n");
-               with_error = false;
+               dw_dev->frame_failed = false;
                err_irqs &= ~DW100_INTERRUPT_STATUS_INT_ERR_STATUS
                        (DW100_INTERRUPT_STATUS_INT_ERR_FRAME_DONE);
        }
@@ -1425,7 +1428,14 @@ static irqreturn_t dw100_irq_handler(int irq, void 
*dev_id)
        dw100_hw_clear_irq(dw_dev, pending_irqs |
                           DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT);
 
-       dw100_job_finish(dw_dev, with_error);
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t dw100_irq_thread_fn(int irq, void *dev_id)
+{
+       struct dw100_device *dw_dev = dev_id;
+
+       dw100_job_finish(dw_dev, dw_dev->frame_failed);
 
        return IRQ_HANDLED;
 }
@@ -1594,8 +1604,9 @@ static int dw100_probe(struct platform_device *pdev)
 
        pm_runtime_put_sync(&pdev->dev);
 
-       ret = devm_request_irq(&pdev->dev, irq, dw100_irq_handler, IRQF_ONESHOT,
-                              dev_name(&pdev->dev), dw_dev);
+       ret = devm_request_threaded_irq(&pdev->dev, irq, dw100_irq_handler,
+                                       dw100_irq_thread_fn, 0,
+                                       dev_name(&pdev->dev), dw_dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
                goto err_pm;
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to