[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: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
This commit is contained in:
Laurent Pinchart 2014-07-23 10:30:57 -03:00 committed by Mauro Carvalho Chehab
parent d903a0a367
commit cd73bb6c4e
2 changed files with 34 additions and 12 deletions

View File

@ -1062,10 +1062,13 @@ static void resizer_isr_buffer(struct isp_res_device *res)
void omap3isp_resizer_isr(struct isp_res_device *res) void omap3isp_resizer_isr(struct isp_res_device *res)
{ {
struct v4l2_mbus_framefmt *informat, *outformat; struct v4l2_mbus_framefmt *informat, *outformat;
unsigned long flags;
if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
return; return;
spin_lock_irqsave(&res->lock, flags);
if (res->applycrop) { if (res->applycrop) {
outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
V4L2_SUBDEV_FORMAT_ACTIVE); V4L2_SUBDEV_FORMAT_ACTIVE);
@ -1075,6 +1078,8 @@ void omap3isp_resizer_isr(struct isp_res_device *res)
res->applycrop = 0; res->applycrop = 0;
} }
spin_unlock_irqrestore(&res->lock, flags);
resizer_isr_buffer(res); 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_res_device *res = v4l2_get_subdevdata(sd);
struct isp_device *isp = to_isp_device(res); 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; struct resizer_ratio ratio;
unsigned long flags;
if (sel->target != V4L2_SEL_TGT_CROP || if (sel->target != V4L2_SEL_TGT_CROP ||
sel->pad != RESZ_PAD_SINK) 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, format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
sel->which); sel->which);
format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE,
sel->which); sel->which);
dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n", 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", __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
format_sink->width, format_sink->height, format_sink->width, format_sink->height,
sel->r.left, sel->r.top, sel->r.width, sel->r.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 /* Clamp the crop rectangle to the bounds, and then mangle it further to
* fulfill the TRM equations. Store the clamped but otherwise unmangled * 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 * smaller input crop rectangle every time the output size is set if we
* stored the mangled rectangle. * 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_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", 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", __func__, sel->which == V4L2_SUBDEV_FORMAT_TRY ? "try" : "act",
format_sink->width, format_sink->height, format_sink->width, format_sink->height,
sel->r.left, sel->r.top, sel->r.width, sel->r.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; 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->ratio = ratio;
res->crop.active = sel->r; 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) if (res->state != ISP_PIPELINE_STREAM_STOPPED)
res->applycrop = 1; res->applycrop = 1;
spin_unlock_irqrestore(&res->lock, flags);
return 0; return 0;
} }
@ -1772,6 +1789,8 @@ int omap3isp_resizer_init(struct isp_device *isp)
init_waitqueue_head(&res->wait); init_waitqueue_head(&res->wait);
atomic_set(&res->stopping, 0); atomic_set(&res->stopping, 0);
spin_lock_init(&res->lock);
return resizer_init_entities(res); return resizer_init_entities(res);
} }

View File

@ -17,6 +17,7 @@
#ifndef OMAP3_ISP_RESIZER_H #ifndef OMAP3_ISP_RESIZER_H
#define OMAP3_ISP_RESIZER_H #define OMAP3_ISP_RESIZER_H
#include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
/* /*
@ -86,6 +87,7 @@ enum resizer_input_entity {
/* /*
* struct isp_res_device - OMAP3 ISP resizer module * 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.request: Crop rectangle requested by the user
* @crop.active: Active crop rectangle (based on hardware requirements) * @crop.active: Active crop rectangle (based on hardware requirements)
*/ */
@ -106,6 +108,7 @@ struct isp_res_device {
enum isp_pipeline_stream_state state; enum isp_pipeline_stream_state state;
wait_queue_head_t wait; wait_queue_head_t wait;
atomic_t stopping; atomic_t stopping;
spinlock_t lock;
struct { struct {
struct v4l2_rect request; struct v4l2_rect request;