Commit e2c91d4d authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

[media] media-device: get rid of the spinlock

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: [<ffffffff81ce8933>] __driver_attach+0xa3/0x160
 #1:  (&dev->mutex){......}, at: [<ffffffff81ce8941>] __driver_attach+0xb1/0x160
 #2:  (register_mutex#5){+.+.+.}, at: [<ffffffffa10596c7>] usb_audio_probe+0x257/0x1c90 [snd_usb_audio]
 #3:  (&(&mdev->lock)->rlock){+.+.+.}, at: [<ffffffffa0e6051b>] 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
 0000000000000000 ffff8803b3f6f288 ffffffff81933901 ffff8803c4bae000
 ffff8803c4bae5c8 ffff8803b3f6f2b0 ffffffff811c6af5 ffff8803c4bae000
 ffffffff8285d7f6 0000000000000509 ffff8803b3f6f2f0 ffffffff811c6ce5
 Call Trace:
 [<ffffffff81933901>] dump_stack+0x85/0xc4
 [<ffffffff811c6af5>] ___might_sleep+0x245/0x3a0
 [<ffffffff811c6ce5>] __might_sleep+0x95/0x1a0
 [<ffffffff8155aade>] kmem_cache_alloc_trace+0x20e/0x300
 [<ffffffffa0e66e3d>] ? media_add_link+0x4d/0x140 [media]
 [<ffffffffa0e66e3d>] media_add_link+0x4d/0x140 [media]
 [<ffffffffa0e69931>] media_create_pad_link+0xa1/0x600 [media]
 [<ffffffffa0fe11b3>] au0828_media_graph_notify+0x173/0x360 [au0828]
 [<ffffffffa0e68a6a>] ? media_gobj_create+0x1ba/0x480 [media]
 [<ffffffffa0e606fb>] media_device_register_entity+0x3ab/0x700 [media]
Reviewed-by: default avatarJavier Martinez Canillas <javier@osg.samsung.com>
Acked-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent ecb7b018
...@@ -90,18 +90,13 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) ...@@ -90,18 +90,13 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id)
id &= ~MEDIA_ENT_ID_FLAG_NEXT; id &= ~MEDIA_ENT_ID_FLAG_NEXT;
spin_lock(&mdev->lock);
media_device_for_each_entity(entity, mdev) { media_device_for_each_entity(entity, mdev) {
if (((media_entity_id(entity) == id) && !next) || if (((media_entity_id(entity) == id) && !next) ||
((media_entity_id(entity) > id) && next)) { ((media_entity_id(entity) > id) && next)) {
spin_unlock(&mdev->lock);
return entity; return entity;
} }
} }
spin_unlock(&mdev->lock);
return NULL; return NULL;
} }
...@@ -431,6 +426,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, ...@@ -431,6 +426,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
struct media_device *dev = to_media_device(devnode); struct media_device *dev = to_media_device(devnode);
long ret; long ret;
mutex_lock(&dev->graph_mutex);
switch (cmd) { switch (cmd) {
case MEDIA_IOC_DEVICE_INFO: case MEDIA_IOC_DEVICE_INFO:
ret = media_device_get_info(dev, ret = media_device_get_info(dev,
...@@ -443,29 +439,24 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, ...@@ -443,29 +439,24 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
break; break;
case MEDIA_IOC_ENUM_LINKS: case MEDIA_IOC_ENUM_LINKS:
mutex_lock(&dev->graph_mutex);
ret = media_device_enum_links(dev, ret = media_device_enum_links(dev,
(struct media_links_enum __user *)arg); (struct media_links_enum __user *)arg);
mutex_unlock(&dev->graph_mutex);
break; break;
case MEDIA_IOC_SETUP_LINK: case MEDIA_IOC_SETUP_LINK:
mutex_lock(&dev->graph_mutex);
ret = media_device_setup_link(dev, ret = media_device_setup_link(dev,
(struct media_link_desc __user *)arg); (struct media_link_desc __user *)arg);
mutex_unlock(&dev->graph_mutex);
break; break;
case MEDIA_IOC_G_TOPOLOGY: case MEDIA_IOC_G_TOPOLOGY:
mutex_lock(&dev->graph_mutex);
ret = media_device_get_topology(dev, ret = media_device_get_topology(dev,
(struct media_v2_topology __user *)arg); (struct media_v2_topology __user *)arg);
mutex_unlock(&dev->graph_mutex);
break; break;
default: default:
ret = -ENOIOCTLCMD; ret = -ENOIOCTLCMD;
} }
mutex_unlock(&dev->graph_mutex);
return ret; return ret;
} }
...@@ -590,12 +581,12 @@ int __must_check media_device_register_entity(struct media_device *mdev, ...@@ -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)) if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
ret = ida_get_new_above(&mdev->entity_internal_idx, 1, ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
&entity->internal_idx); &entity->internal_idx);
if (ret < 0) { if (ret < 0) {
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
return ret; return ret;
} }
...@@ -615,9 +606,6 @@ int __must_check media_device_register_entity(struct media_device *mdev, ...@@ -615,9 +606,6 @@ int __must_check media_device_register_entity(struct media_device *mdev,
(notify)->notify(entity, notify->notify_data); (notify)->notify(entity, notify->notify_data);
} }
spin_unlock(&mdev->lock);
mutex_lock(&mdev->graph_mutex);
if (mdev->entity_internal_idx_max if (mdev->entity_internal_idx_max
>= mdev->pm_count_walk.ent_enum.idx_max) { >= mdev->pm_count_walk.ent_enum.idx_max) {
struct media_entity_graph new = { .top = 0 }; struct media_entity_graph new = { .top = 0 };
...@@ -680,9 +668,9 @@ void media_device_unregister_entity(struct media_entity *entity) ...@@ -680,9 +668,9 @@ void media_device_unregister_entity(struct media_entity *entity)
if (mdev == NULL) if (mdev == NULL)
return; return;
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
__media_device_unregister_entity(entity); __media_device_unregister_entity(entity);
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
} }
EXPORT_SYMBOL_GPL(media_device_unregister_entity); EXPORT_SYMBOL_GPL(media_device_unregister_entity);
...@@ -703,7 +691,6 @@ void media_device_init(struct media_device *mdev) ...@@ -703,7 +691,6 @@ void media_device_init(struct media_device *mdev)
INIT_LIST_HEAD(&mdev->pads); INIT_LIST_HEAD(&mdev->pads);
INIT_LIST_HEAD(&mdev->links); INIT_LIST_HEAD(&mdev->links);
INIT_LIST_HEAD(&mdev->entity_notify); INIT_LIST_HEAD(&mdev->entity_notify);
spin_lock_init(&mdev->lock);
mutex_init(&mdev->graph_mutex); mutex_init(&mdev->graph_mutex);
ida_init(&mdev->entity_internal_idx); ida_init(&mdev->entity_internal_idx);
...@@ -752,9 +739,9 @@ EXPORT_SYMBOL_GPL(__media_device_register); ...@@ -752,9 +739,9 @@ EXPORT_SYMBOL_GPL(__media_device_register);
int __must_check media_device_register_entity_notify(struct media_device *mdev, int __must_check media_device_register_entity_notify(struct media_device *mdev,
struct media_entity_notify *nptr) struct media_entity_notify *nptr)
{ {
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
list_add_tail(&nptr->list, &mdev->entity_notify); list_add_tail(&nptr->list, &mdev->entity_notify);
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(media_device_register_entity_notify); EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
...@@ -771,9 +758,9 @@ static void __media_device_unregister_entity_notify(struct media_device *mdev, ...@@ -771,9 +758,9 @@ static void __media_device_unregister_entity_notify(struct media_device *mdev,
void media_device_unregister_entity_notify(struct media_device *mdev, void media_device_unregister_entity_notify(struct media_device *mdev,
struct media_entity_notify *nptr) struct media_entity_notify *nptr)
{ {
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
__media_device_unregister_entity_notify(mdev, nptr); __media_device_unregister_entity_notify(mdev, nptr);
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
} }
EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify); EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
...@@ -787,11 +774,11 @@ void media_device_unregister(struct media_device *mdev) ...@@ -787,11 +774,11 @@ void media_device_unregister(struct media_device *mdev)
if (mdev == NULL) if (mdev == NULL)
return; return;
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
/* Check if mdev was ever registered at all */ /* Check if mdev was ever registered at all */
if (!media_devnode_is_registered(&mdev->devnode)) { if (!media_devnode_is_registered(&mdev->devnode)) {
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
return; return;
} }
...@@ -811,7 +798,7 @@ void media_device_unregister(struct media_device *mdev) ...@@ -811,7 +798,7 @@ void media_device_unregister(struct media_device *mdev)
kfree(intf); kfree(intf);
} }
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
device_remove_file(&mdev->devnode.dev, &dev_attr_model); device_remove_file(&mdev->devnode.dev, &dev_attr_model);
media_devnode_unregister(&mdev->devnode); media_devnode_unregister(&mdev->devnode);
......
...@@ -219,7 +219,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, ...@@ -219,7 +219,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
entity->pads = pads; entity->pads = pads;
if (mdev) if (mdev)
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
for (i = 0; i < num_pads; i++) { for (i = 0; i < num_pads; i++) {
pads[i].entity = entity; pads[i].entity = entity;
...@@ -230,7 +230,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, ...@@ -230,7 +230,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
} }
if (mdev) if (mdev)
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
return 0; return 0;
} }
...@@ -747,9 +747,9 @@ void media_entity_remove_links(struct media_entity *entity) ...@@ -747,9 +747,9 @@ void media_entity_remove_links(struct media_entity *entity)
if (mdev == NULL) if (mdev == NULL)
return; return;
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
__media_entity_remove_links(entity); __media_entity_remove_links(entity);
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
} }
EXPORT_SYMBOL_GPL(media_entity_remove_links); EXPORT_SYMBOL_GPL(media_entity_remove_links);
...@@ -951,9 +951,9 @@ void media_remove_intf_link(struct media_link *link) ...@@ -951,9 +951,9 @@ void media_remove_intf_link(struct media_link *link)
if (mdev == NULL) if (mdev == NULL)
return; return;
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
__media_remove_intf_link(link); __media_remove_intf_link(link);
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
} }
EXPORT_SYMBOL_GPL(media_remove_intf_link); EXPORT_SYMBOL_GPL(media_remove_intf_link);
...@@ -975,8 +975,8 @@ void media_remove_intf_links(struct media_interface *intf) ...@@ -975,8 +975,8 @@ void media_remove_intf_links(struct media_interface *intf)
if (mdev == NULL) if (mdev == NULL)
return; return;
spin_lock(&mdev->lock); mutex_lock(&mdev->graph_mutex);
__media_remove_intf_links(intf); __media_remove_intf_links(intf);
spin_unlock(&mdev->lock); mutex_unlock(&mdev->graph_mutex);
} }
EXPORT_SYMBOL_GPL(media_remove_intf_links); EXPORT_SYMBOL_GPL(media_remove_intf_links);
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h>
#include <media/media-devnode.h> #include <media/media-devnode.h>
#include <media/media-entity.h> #include <media/media-entity.h>
...@@ -304,8 +303,7 @@ struct media_entity_notify { ...@@ -304,8 +303,7 @@ struct media_entity_notify {
* @pads: List of registered pads * @pads: List of registered pads
* @links: List of registered links * @links: List of registered links
* @entity_notify: List of registered entity_notify callbacks * @entity_notify: List of registered entity_notify callbacks
* @lock: Entities list lock * @graph_mutex: Protects access to struct media_device data
* @graph_mutex: Entities graph operation lock
* @pm_count_walk: Graph walk for power state walk. Access serialised using * @pm_count_walk: Graph walk for power state walk. Access serialised using
* graph_mutex. * graph_mutex.
* *
...@@ -371,8 +369,6 @@ struct media_device { ...@@ -371,8 +369,6 @@ struct media_device {
/* notify callback list invoked when a new entity is registered */ /* notify callback list invoked when a new entity is registered */
struct list_head entity_notify; struct list_head entity_notify;
/* Protects the graph objects creation/removal */
spinlock_t lock;
/* Serializes graph operations. */ /* Serializes graph operations. */
struct mutex graph_mutex; struct mutex graph_mutex;
struct media_entity_graph pm_count_walk; struct media_entity_graph pm_count_walk;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment