On 04/06/2016 03:55 PM, Mauro Carvalho Chehab wrote:
> Right now, the lock schema for media_device struct is messy,
> since sometimes, it is protected via a spin lock, while, for
> media graph traversal, it is protected by a mutex.
>
> Solve this conflict by always using a mutex.
>
> As a side effect, this prevents a bug when the media notifiers
> is called at atomic context, while running the notifier callback:
>
> BUG: sleeping function called from invalid context at mm/slub.c:1289
> in_atomic(): 1, irqs_disabled(): 0, pid: 3479, name: modprobe
> 4 locks held by modprobe/3479:
> #0: (&dev->mutex){..}, at: []
> __driver_attach+0xa3/0x160
> #1: (&dev->mutex){..}, at: []
> __driver_attach+0xb1/0x160
> #2: (register_mutex#5){+.+.+.}, at: []
> usb_audio_probe+0x257/0x1c90 [snd_usb_audio]
> #3: (&(&mdev->lock)->rlock){+.+.+.}, at: []
> media_device_register_entity+0x1cb/0x700 [media]
> CPU: 2 PID: 3479 Comm: modprobe Not tainted 4.5.0-rc3+ #49
> Hardware name: /NUC5i7RYB, BIOS
> RYBDWi35.86A.0350.2015.0812.1722 08/12/2015
> 8803b3f6f288 81933901 8803c4bae000
> 8803c4bae5c8 8803b3f6f2b0 811c6af5 8803c4bae000
> 8285d7f6 0509 8803b3f6f2f0 811c6ce5
> Call Trace:
> [] dump_stack+0x85/0xc4
> [] ___might_sleep+0x245/0x3a0
> [] __might_sleep+0x95/0x1a0
> [] kmem_cache_alloc_trace+0x20e/0x300
> [] ? media_add_link+0x4d/0x140 [media]
> [] media_add_link+0x4d/0x140 [media]
> [] media_create_pad_link+0xa1/0x600 [media]
> [] au0828_media_graph_notify+0x173/0x360 [au0828]
> [] ? media_gobj_create+0x1ba/0x480 [media]
> [] media_device_register_entity+0x3ab/0x700 [media]
>
> Reviewed-by: Javier Martinez Canillas
> Acked-by: Sakari Ailus
> Signed-off-by: Mauro Carvalho Chehab
Acked-by: Hans Verkuil
Regards,
Hans
> ---
> drivers/media/media-device.c | 39 +--
> drivers/media/media-entity.c | 16
> include/media/media-device.h | 6 +-
> 3 files changed, 22 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index 6e43c95629ea..898a3cf814ba 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -90,18 +90,13 @@ static struct media_entity *find_entity(struct
> media_device *mdev, u32 id)
>
> id &= ~MEDIA_ENT_ID_FLAG_NEXT;
>
> - spin_lock(&mdev->lock);
> -
> media_device_for_each_entity(entity, mdev) {
> if (((media_entity_id(entity) == id) && !next) ||
> ((media_entity_id(entity) > id) && next)) {
> - spin_unlock(&mdev->lock);
> return entity;
> }
> }
>
> - spin_unlock(&mdev->lock);
> -
> return NULL;
> }
>
> @@ -431,6 +426,7 @@ static long media_device_ioctl(struct file *filp,
> unsigned int cmd,
> struct media_device *dev = to_media_device(devnode);
> long ret;
>
> + mutex_lock(&dev->graph_mutex);
> switch (cmd) {
> case MEDIA_IOC_DEVICE_INFO:
> ret = media_device_get_info(dev,
> @@ -443,29 +439,24 @@ static long media_device_ioctl(struct file *filp,
> unsigned int cmd,
> break;
>
> case MEDIA_IOC_ENUM_LINKS:
> - mutex_lock(&dev->graph_mutex);
> ret = media_device_enum_links(dev,
> (struct media_links_enum __user *)arg);
> - mutex_unlock(&dev->graph_mutex);
> break;
>
> case MEDIA_IOC_SETUP_LINK:
> - mutex_lock(&dev->graph_mutex);
> ret = media_device_setup_link(dev,
> (struct media_link_desc __user *)arg);
> - mutex_unlock(&dev->graph_mutex);
> break;
>
> case MEDIA_IOC_G_TOPOLOGY:
> - mutex_lock(&dev->graph_mutex);
> ret = media_device_get_topology(dev,
> (struct media_v2_topology __user *)arg);
> - mutex_unlock(&dev->graph_mutex);
> break;
>
> default:
> ret = -ENOIOCTLCMD;
> }
> + mutex_unlock(&dev->graph_mutex);
>
> return ret;
> }
> @@ -590,12 +581,12 @@ int __must_check media_device_register_entity(struct
> media_device *mdev,
> if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
> return -ENOMEM;
>
> - spin_lock(&mdev->lock);
> + mutex_lock(&mdev->graph_mutex);
>
> ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
> &entity->internal_idx);
> if (ret < 0) {
> - spin_unlock(&mdev->lock);
> + mutex_unlock(&mdev->graph_mutex);
> return ret;
> }
>
> @@ -615,9 +606,6 @@ int __must_check media_device_register_entity(struct
> media_device *mdev,
> (notify)->notify(entity, notify->notify_d