Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd73bb6c authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

[media] omap3isp: resizer: Protect against races when updating crop



When updating the crop rectangle during streaming, the IRQ handler will
reprogram the resizer after the current frame. A race condition
currently exists between the set selection operation and the IRQ
handler: if the set selection operation is called twice in a row and the
IRQ handler runs only during the second call, it could reprogram the
hardware with partially updated values. Use a spinlock to protect
against that.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent d903a0a3
Loading
Loading
Loading
Loading
+31 −12
Original line number Diff line number Diff line
@@ -1062,10 +1062,13 @@ static void resizer_isr_buffer(struct isp_res_device *res)
void omap3isp_resizer_isr(struct isp_res_device *res)
{
	struct v4l2_mbus_framefmt *informat, *outformat;
	unsigned long flags;

	if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
		return;

	spin_lock_irqsave(&res->lock, flags);

	if (res->applycrop) {
		outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
					      V4L2_SUBDEV_FORMAT_ACTIVE);
@@ -1075,6 +1078,8 @@ void omap3isp_resizer_isr(struct isp_res_device *res)
		res->applycrop = 0;
	}

	spin_unlock_irqrestore(&res->lock, flags);

	resizer_isr_buffer(res);
}

@@ -1277,8 +1282,10 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
{
	struct isp_res_device *res = v4l2_get_subdevdata(sd);
	struct isp_device *isp = to_isp_device(res);
	struct v4l2_mbus_framefmt *format_sink, *format_source;
	const struct v4l2_mbus_framefmt *format_sink;
	struct v4l2_mbus_framefmt format_source;
	struct resizer_ratio ratio;
	unsigned long flags;

	if (sel->target != V4L2_SEL_TGT_CROP ||
	    sel->pad != RESZ_PAD_SINK)
@@ -1286,14 +1293,14 @@ static int resizer_set_selection(struct v4l2_subdev *sd,

	format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
					   sel->which);
	format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
	format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE,
					      sel->which);

	dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
		__func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
		format_sink->width, format_sink->height,
		sel->r.left, sel->r.top, sel->r.width, sel->r.height,
		format_source->width, format_source->height);
		format_source.width, format_source.height);

	/* Clamp the crop rectangle to the bounds, and then mangle it further to
	 * fulfill the TRM equations. Store the clamped but otherwise unmangled
@@ -1303,29 +1310,39 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
	 * smaller input crop rectangle every time the output size is set if we
	 * stored the mangled rectangle.
	 */
	resizer_try_crop(format_sink, format_source, &sel->r);
	resizer_try_crop(format_sink, &format_source, &sel->r);
	*__resizer_get_crop(res, fh, sel->which) = sel->r;
	resizer_calc_ratios(res, &sel->r, format_source, &ratio);
	resizer_calc_ratios(res, &sel->r, &format_source, &ratio);

	dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
		__func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
		format_sink->width, format_sink->height,
		sel->r.left, sel->r.top, sel->r.width, sel->r.height,
		format_source->width, format_source->height);
		format_source.width, format_source.height);

	if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
		*__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
			format_source;
		return 0;
	}

	/* Update the source format, resizing ratios and crop rectangle. If
	 * streaming is on the IRQ handler will reprogram the resizer after the
	 * current frame. We thus we need to protect against race conditions.
	 */
	spin_lock_irqsave(&res->lock, flags);

	*__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
		format_source;

	res->ratio = ratio;
	res->crop.active = sel->r;

	/*
	 * set_selection can be called while streaming is on. In this case the
	 * crop values will be set in the next IRQ.
	 */
	if (res->state != ISP_PIPELINE_STREAM_STOPPED)
		res->applycrop = 1;

	spin_unlock_irqrestore(&res->lock, flags);

	return 0;
}

@@ -1772,6 +1789,8 @@ int omap3isp_resizer_init(struct isp_device *isp)

	init_waitqueue_head(&res->wait);
	atomic_set(&res->stopping, 0);
	spin_lock_init(&res->lock);

	return resizer_init_entities(res);
}

+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef OMAP3_ISP_RESIZER_H
#define OMAP3_ISP_RESIZER_H

#include <linux/spinlock.h>
#include <linux/types.h>

/*
@@ -86,6 +87,7 @@ enum resizer_input_entity {

/*
 * struct isp_res_device - OMAP3 ISP resizer module
 * @lock: Protects formats and crop rectangles between set_selection and IRQ
 * @crop.request: Crop rectangle requested by the user
 * @crop.active: Active crop rectangle (based on hardware requirements)
 */
@@ -106,6 +108,7 @@ struct isp_res_device {
	enum isp_pipeline_stream_state state;
	wait_queue_head_t wait;
	atomic_t stopping;
	spinlock_t lock;

	struct {
		struct v4l2_rect request;