[media] media-device: better lock media_device_unregister()
authorMauro Carvalho Chehab <mchehab@osg.samsung.com>
Tue, 15 Dec 2015 10:36:51 +0000 (08:36 -0200)
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>
Mon, 11 Jan 2016 14:19:15 +0000 (12:19 -0200)
If media_device_unregister() is called by two different
drivers, a race condition may happen, as the check if the
device is not registered is not protected.

Move the spin_lock() to happen earlier in the function, in order
to prevent such race condition.

Reported-by: Shuah Khan <shuahkh@osg.samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
drivers/media/media-device.c

index 1222fa642ad843560494eb3619f3907997f614ce..189c2ba8c3d35395fa54bab5b207e3609051b510 100644 (file)
@@ -573,18 +573,13 @@ EXPORT_SYMBOL_GPL(media_device_register_entity);
  * If the entity has never been registered this function will return
  * immediately.
  */
-void media_device_unregister_entity(struct media_entity *entity)
+static void __media_device_unregister_entity(struct media_entity *entity)
 {
        struct media_device *mdev = entity->graph_obj.mdev;
        struct media_link *link, *tmp;
        struct media_interface *intf;
        unsigned int i;
 
-       if (mdev == NULL)
-               return;
-
-       spin_lock(&mdev->lock);
-
        /* Remove all interface links pointing to this entity */
        list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) {
                list_for_each_entry_safe(link, tmp, &intf->links, list) {
@@ -603,11 +598,23 @@ void media_device_unregister_entity(struct media_entity *entity)
        /* Remove the entity */
        media_gobj_destroy(&entity->graph_obj);
 
-       spin_unlock(&mdev->lock);
        entity->graph_obj.mdev = NULL;
 }
+
+void media_device_unregister_entity(struct media_entity *entity)
+{
+       struct media_device *mdev = entity->graph_obj.mdev;
+
+       if (mdev == NULL)
+               return;
+
+       spin_lock(&mdev->lock);
+       __media_device_unregister_entity(entity);
+       spin_unlock(&mdev->lock);
+}
 EXPORT_SYMBOL_GPL(media_device_unregister_entity);
 
+
 /**
  * media_device_register - register a media device
  * @mdev:      The media device
@@ -666,22 +673,29 @@ void media_device_unregister(struct media_device *mdev)
        struct media_entity *next;
        struct media_interface *intf, *tmp_intf;
 
+       if (mdev == NULL)
+               return;
+
+       spin_lock(&mdev->lock);
+
        /* 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);
                return;
+       }
 
        /* Remove all entities from the media device */
        list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
-               media_device_unregister_entity(entity);
+               __media_device_unregister_entity(entity);
 
        /* Remove all interfaces from the media device */
-       spin_lock(&mdev->lock);
        list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
                                 graph_obj.list) {
                __media_remove_intf_links(intf);
                media_gobj_destroy(&intf->graph_obj);
                kfree(intf);
        }
+
        spin_unlock(&mdev->lock);
 
        device_remove_file(&mdev->devnode.dev, &dev_attr_model);