Re: [PATCH] [media] media-device: Don't call notify callbacks holding a spinlock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Mauro,

On Monday 14 March 2016 16:16:29 Mauro Carvalho Chehab wrote:
> The notify routines may sleep. So, they can't be called in spinlock
> context. Also, they may want to call other media routines that might
> be spinning the spinlock, like creating a link.
> 
> This fixes the following bug:
> 
> [ 1839.510587] BUG: sleeping function called from invalid context at
> mm/slub.c:1289 [ 1839.510881] in_atomic(): 1, irqs_disabled(): 0, pid:
> 3479, name: modprobe [ 1839.511157] 4 locks held by modprobe/3479:
> [ 1839.511415]  #0:  (&dev->mutex){......}, at: [<ffffffff81ce8933>]
> __driver_attach+0xa3/0x160 [ 1839.512381]  #1:  (&dev->mutex){......}, at:
> [<ffffffff81ce8941>] __driver_attach+0xb1/0x160 [ 1839.512388]  #2: 
> (register_mutex#5){+.+.+.}, at: [<ffffffffa10596c7>]
> usb_audio_probe+0x257/0x1c90 [snd_usb_audio] [ 1839.512401]  #3: 
> (&(&mdev->lock)->rlock){+.+.+.}, at: [<ffffffffa0e6051b>]
> media_device_register_entity+0x1cb/0x700 [media] [ 1839.512412] CPU: 2 PID:
> 3479 Comm: modprobe Not tainted 4.5.0-rc3+ #49 [ 1839.512415] Hardware
> name:                  /NUC5i7RYB, BIOS RYBDWi35.86A.0350.2015.0812.1722
> 08/12/2015 [ 1839.512417]  0000000000000000 ffff8803b3f6f288
> ffffffff81933901 ffff8803c4bae000 [ 1839.512422]  ffff8803c4bae5c8
> ffff8803b3f6f2b0 ffffffff811c6af5 ffff8803c4bae000 [ 1839.512427] 
> ffffffff8285d7f6 0000000000000509 ffff8803b3f6f2f0 ffffffff811c6ce5 [
> 1839.512432] Call Trace:
> [ 1839.512436]  [<ffffffff81933901>] dump_stack+0x85/0xc4
> [ 1839.512440]  [<ffffffff811c6af5>] ___might_sleep+0x245/0x3a0
> [ 1839.512443]  [<ffffffff811c6ce5>] __might_sleep+0x95/0x1a0
> [ 1839.512446]  [<ffffffff8155aade>] kmem_cache_alloc_trace+0x20e/0x300
> [ 1839.512451]  [<ffffffffa0e66e3d>] ? media_add_link+0x4d/0x140 [media]
> [ 1839.512455]  [<ffffffffa0e66e3d>] media_add_link+0x4d/0x140 [media]
> [ 1839.512459]  [<ffffffffa0e69931>] media_create_pad_link+0xa1/0x600
> [media] [ 1839.512463]  [<ffffffffa0fe11b3>]
> au0828_media_graph_notify+0x173/0x360 [au0828] [ 1839.512467] 
> [<ffffffffa0e68a6a>] ? media_gobj_create+0x1ba/0x480 [media] [ 1839.512471]
>  [<ffffffffa0e606fb>] media_device_register_entity+0x3ab/0x700 [media]
> 
> Tested with an HVR-950Q, under the following testcases:
> 
> 1) load au0828 driver first, and then snd-usb-audio;
> 2) load snd-usb-audio driver first, and then au0828;
> 3) loading both drivers, and then connecting the device.
> 
> Signed-off-by: Mauro Carvalho Chehab <mchehab@xxxxxxxxxxxxxxx>
> ---
>  drivers/media/i2c/tvp5150.c           | 39 ++++++++++++++++++++------------
>  drivers/media/media-device.c          |  5 +++--
>  drivers/media/v4l2-core/v4l2-common.c |  8 +++++++
>  include/media/v4l2-subdev.h           |  4 ++++
>  4 files changed, 40 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> index ff18444e19e4..14004fd7d0fb 100644
> --- a/drivers/media/i2c/tvp5150.c
> +++ b/drivers/media/i2c/tvp5150.c
> @@ -1208,6 +1208,28 @@ static int tvp5150_registered_async(struct
> v4l2_subdev *sd) return 0;
>  }
> 
> +static int __maybe_unused tvp5150_pad_init(struct v4l2_subdev *sd)
> +{
> +	struct tvp5150 *core = to_tvp5150(sd);
> +	int res;
> +
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	core->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
> +	core->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
> +	core->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
> +
> +	res = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, core->pads);
> +	if (res < 0)
> +		return res;
> +
> +	sd->entity.ops = &tvp5150_sd_media_ops;
> +#endif
> +	return 0;
> +}
> +
> +
>  /* -----------------------------------------------------------------------
> */
> 
>  static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
> @@ -1246,6 +1268,9 @@ static const struct v4l2_subdev_vbi_ops
> tvp5150_vbi_ops = { };
> 
>  static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	.pad_init = tvp5150_pad_init,
> +#endif
>  	.enum_mbus_code = tvp5150_enum_mbus_code,
>  	.enum_frame_size = tvp5150_enum_frame_size,
>  	.set_fmt = tvp5150_fill_fmt,
> @@ -1475,20 +1500,6 @@ static int tvp5150_probe(struct i2c_client *c,
>  	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
>  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> 
> -#if defined(CONFIG_MEDIA_CONTROLLER)
> -	core->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
> -	core->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
> -	core->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
> -
> -	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
> -
> -	res = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, core->pads);
> -	if (res < 0)
> -		return res;
> -
> -	sd->entity.ops = &tvp5150_sd_media_ops;
> -#endif
> -
>  	res = tvp5150_detect_version(core);
>  	if (res < 0)
>  		return res;
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index 6ba6e8f982fc..fc3c199e5500 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -587,14 +587,15 @@ int __must_check media_device_register_entity(struct
> media_device *mdev, media_gobj_create(mdev, MEDIA_GRAPH_PAD,
>  			       &entity->pads[i].graph_obj);
> 
> +	spin_unlock(&mdev->lock);
> +
>  	/* invoke entity_notify callbacks */
>  	list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) {
>  		(notify)->notify(entity, notify->notify_data);
>  	}

I don't think this is the right approach. There's no lock protecting the 
entity_notify list, and nothing that prevents the notifiers from touching the 
entity_notify list. The list_for_each_entry_safe() function doesn't help you 
there, it only protects against deletion of the current list entry.

Locking needs to be audited and fixed, let's not push a bug fix that 
introduces another bug.

Furthermore, I wonder if we really need notifiers. They add complexity and are 
difficult to get right, isn't there another possible approach ?

> -	spin_unlock(&mdev->lock);
> -
>  	mutex_lock(&mdev->graph_mutex);
> +
>  	if (mdev->entity_internal_idx_max
> 
>  	    >= mdev->pm_count_walk.ent_enum.idx_max) {
> 
>  		struct media_entity_graph new = { .top = 0 };
> diff --git a/drivers/media/v4l2-core/v4l2-common.c
> b/drivers/media/v4l2-core/v4l2-common.c index 5b808500e7e7..6bcdf557e027
> 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -112,6 +112,14 @@ EXPORT_SYMBOL(v4l2_ctrl_query_fill);
>  void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client
> *client, const struct v4l2_subdev_ops *ops)
>  {
> +	/*
> +	 * Initialize the MC pads - for now, this will be called
> +	 * unconditionally, since the current implementation will defer
> +	 * the pads initialization until the media_dev is created.
> +	 */
> +	if (v4l2_subdev_has_op(sd, pad, pad_init))
> +		sd->ops->pad->pad_init(sd);
> +
>  	v4l2_subdev_init(sd, ops);
>  	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
>  	/* the owner is the same as the i2c_client's driver owner */
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index 11e2dfec0198..c9bb221029e4 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -572,6 +572,9 @@ struct v4l2_subdev_pad_config {
>  /**
>   * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations
>   *
> + * @pad_init: callback that initializes the media-controller specific part
> + *	      of the subdev driver, creating its pads
> + *
>   * @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE ioctl handler
> *		    code.
>   * @enum_frame_size: callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE ioctl
> handler @@ -607,6 +610,7 @@ struct v4l2_subdev_pad_config {
>   *                  may be adjusted by the subdev driver to device
> capabilities. */
>  struct v4l2_subdev_pad_ops {
> +	int (*pad_init)(struct v4l2_subdev *sd);
>  	int (*enum_mbus_code)(struct v4l2_subdev *sd,
>  			      struct v4l2_subdev_pad_config *cfg,
>  			      struct v4l2_subdev_mbus_code_enum *code);

-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux