media: cedrus: Add watchdog for job completion

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@gmail.com>
Reviewed-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
This commit is contained in:
Jernej Skrabec
2022-02-01 19:33:24 +01:00
committed by Hans Verkuil
parent 7f9cfb5499
commit 7c38a551bd
5 changed files with 36 additions and 0 deletions

View File

@@ -439,6 +439,8 @@ static int cedrus_probe(struct platform_device *pdev)
mutex_init(&dev->dev_mutex); mutex_init(&dev->dev_mutex);
INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog);
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to register V4L2 device\n"); dev_err(&pdev->dev, "Failed to register V4L2 device\n");

View File

@@ -24,6 +24,7 @@
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/workqueue.h>
#define CEDRUS_NAME "cedrus" #define CEDRUS_NAME "cedrus"
@@ -194,6 +195,8 @@ struct cedrus_dev {
struct reset_control *rstc; struct reset_control *rstc;
unsigned int capabilities; unsigned int capabilities;
struct delayed_work watchdog_work;
}; };
extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2;

View File

@@ -97,4 +97,8 @@ void cedrus_device_run(void *priv)
v4l2_ctrl_request_complete(src_req, &ctx->hdl); v4l2_ctrl_request_complete(src_req, &ctx->hdl);
dev->dec_ops[ctx->current_codec]->trigger(ctx); dev->dec_ops[ctx->current_codec]->trigger(ctx);
/* Start the watchdog timer. */
schedule_delayed_work(&dev->watchdog_work,
msecs_to_jiffies(2000));
} }

View File

@@ -118,6 +118,13 @@ static irqreturn_t cedrus_irq(int irq, void *data)
enum vb2_buffer_state state; enum vb2_buffer_state state;
enum cedrus_irq_status status; 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); ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
if (!ctx) { if (!ctx) {
v4l2_err(&dev->v4l2_dev, v4l2_err(&dev->v4l2_dev,
@@ -143,6 +150,24 @@ static irqreturn_t cedrus_irq(int irq, void *data)
return IRQ_HANDLED; 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) int cedrus_hw_suspend(struct device *device)
{ {
struct cedrus_dev *dev = dev_get_drvdata(device); struct cedrus_dev *dev = dev_get_drvdata(device);

View File

@@ -28,4 +28,6 @@ int cedrus_hw_resume(struct device *device);
int cedrus_hw_probe(struct cedrus_dev *dev); int cedrus_hw_probe(struct cedrus_dev *dev);
void cedrus_hw_remove(struct cedrus_dev *dev); void cedrus_hw_remove(struct cedrus_dev *dev);
void cedrus_watchdog(struct work_struct *work);
#endif #endif