Currently, if job is not completed for whatever reason, userspace application can hang on ioctl and thus become unkillable. In order to prevent that, implement watchdog, which will complete job after 2 seconds with error state. Concept is borrowed from hantro driver. Signed-off-by: Jernej Skrabec <jernej.skrabec@xxxxxxxxx> --- drivers/staging/media/sunxi/cedrus/cedrus.c | 2 ++ drivers/staging/media/sunxi/cedrus/cedrus.h | 3 +++ .../staging/media/sunxi/cedrus/cedrus_dec.c | 4 +++ .../staging/media/sunxi/cedrus/cedrus_hw.c | 25 +++++++++++++++++++ .../staging/media/sunxi/cedrus/cedrus_hw.h | 2 ++ 5 files changed, 36 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 4a4b714b0f26..68b3dcdb5df3 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -439,6 +439,8 @@ static int cedrus_probe(struct platform_device *pdev) mutex_init(&dev->dev_mutex); + INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog); + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index c345f2984041..3bc094eb497f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -24,6 +24,7 @@ #include <linux/iopoll.h> #include <linux/platform_device.h> +#include <linux/workqueue.h> #define CEDRUS_NAME "cedrus" @@ -194,6 +195,8 @@ struct cedrus_dev { struct reset_control *rstc; unsigned int capabilities; + + struct delayed_work watchdog_work; }; extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index a16c1422558f..9c7200299465 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -97,4 +97,8 @@ void cedrus_device_run(void *priv) v4l2_ctrl_request_complete(src_req, &ctx->hdl); dev->dec_ops[ctx->current_codec]->trigger(ctx); + + /* Start the watchdog timer. */ + schedule_delayed_work(&dev->watchdog_work, + msecs_to_jiffies(2000)); } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index 2d7663726467..a6470a89851e 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -118,6 +118,13 @@ static irqreturn_t cedrus_irq(int irq, void *data) enum vb2_buffer_state state; enum cedrus_irq_status status; + /* + * If cancel_delayed_work returns false it means watchdog already + * executed and finished the job. + */ + if (!cancel_delayed_work(&dev->watchdog_work)) + return IRQ_HANDLED; + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); if (!ctx) { v4l2_err(&dev->v4l2_dev, @@ -143,6 +150,24 @@ static irqreturn_t cedrus_irq(int irq, void *data) return IRQ_HANDLED; } +void cedrus_watchdog(struct work_struct *work) +{ + struct cedrus_dev *dev; + struct cedrus_ctx *ctx; + + dev = container_of(to_delayed_work(work), + struct cedrus_dev, watchdog_work); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) + return; + + v4l2_err(&dev->v4l2_dev, "frame processing timed out!\n"); + reset_control_reset(dev->rstc); + v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, + VB2_BUF_STATE_ERROR); +} + int cedrus_hw_suspend(struct device *device) { struct cedrus_dev *dev = dev_get_drvdata(device); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h index 45f641f0bfa2..7c92f00e36da 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h @@ -28,4 +28,6 @@ int cedrus_hw_resume(struct device *device); int cedrus_hw_probe(struct cedrus_dev *dev); void cedrus_hw_remove(struct cedrus_dev *dev); +void cedrus_watchdog(struct work_struct *work); + #endif -- 2.35.1