diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-06-15 04:20:34 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-06-30 22:12:36 -0400 |
commit | c064b8eac8da5d494fd221f14219c4f39502deb2 (patch) | |
tree | f0c3c04d8ee9940d86076ffded0afa78dcca8e7b /drivers/media | |
parent | ca4186f06fecbf2b692a42cdea54b7ef23b2496c (diff) |
[media] v4l: Don't access media entity after is has been destroyed
Entities associated with video device nodes are unregistered in
video_unregister_device(). This destroys the entity even though it can
still be accessed through open video device nodes.
Move the media_device_unregister_entity() call from
video_unregister_device() to v4l2_device_release() to ensure that the
entity isn't unregistered until the last reference to the video device
is released.
Also remove the media_entity_get()/put() calls from v4l2-dev.c. Those
functions were designed for subdevs, to avoid a parent module from being
removed while still accessible through board code. They're not currently
needed for video device nodes, and will oops when a hotpluggable device
is disconnected during streaming, as media_entity_put() called in
v4l2_device_release() tries to access entity->parent->dev->driver which
is set to NULL when the device is disconnected.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/v4l2-dev.c | 39 |
1 files changed, 7 insertions, 32 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 19d5ae293780..06f14008b346 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c | |||
@@ -167,6 +167,12 @@ static void v4l2_device_release(struct device *cd) | |||
167 | 167 | ||
168 | mutex_unlock(&videodev_lock); | 168 | mutex_unlock(&videodev_lock); |
169 | 169 | ||
170 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
171 | if (vdev->v4l2_dev && vdev->v4l2_dev->mdev && | ||
172 | vdev->vfl_type != VFL_TYPE_SUBDEV) | ||
173 | media_device_unregister_entity(&vdev->entity); | ||
174 | #endif | ||
175 | |||
170 | /* Release video_device and perform other | 176 | /* Release video_device and perform other |
171 | cleanups as needed. */ | 177 | cleanups as needed. */ |
172 | vdev->release(vdev); | 178 | vdev->release(vdev); |
@@ -389,9 +395,6 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) | |||
389 | static int v4l2_open(struct inode *inode, struct file *filp) | 395 | static int v4l2_open(struct inode *inode, struct file *filp) |
390 | { | 396 | { |
391 | struct video_device *vdev; | 397 | struct video_device *vdev; |
392 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
393 | struct media_entity *entity = NULL; | ||
394 | #endif | ||
395 | int ret = 0; | 398 | int ret = 0; |
396 | 399 | ||
397 | /* Check if the video device is available */ | 400 | /* Check if the video device is available */ |
@@ -405,17 +408,6 @@ static int v4l2_open(struct inode *inode, struct file *filp) | |||
405 | /* and increase the device refcount */ | 408 | /* and increase the device refcount */ |
406 | video_get(vdev); | 409 | video_get(vdev); |
407 | mutex_unlock(&videodev_lock); | 410 | mutex_unlock(&videodev_lock); |
408 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
409 | if (vdev->v4l2_dev && vdev->v4l2_dev->mdev && | ||
410 | vdev->vfl_type != VFL_TYPE_SUBDEV) { | ||
411 | entity = media_entity_get(&vdev->entity); | ||
412 | if (!entity) { | ||
413 | ret = -EBUSY; | ||
414 | video_put(vdev); | ||
415 | return ret; | ||
416 | } | ||
417 | } | ||
418 | #endif | ||
419 | if (vdev->fops->open) { | 411 | if (vdev->fops->open) { |
420 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { | 412 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { |
421 | ret = -ERESTARTSYS; | 413 | ret = -ERESTARTSYS; |
@@ -431,14 +423,8 @@ static int v4l2_open(struct inode *inode, struct file *filp) | |||
431 | 423 | ||
432 | err: | 424 | err: |
433 | /* decrease the refcount in case of an error */ | 425 | /* decrease the refcount in case of an error */ |
434 | if (ret) { | 426 | if (ret) |
435 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
436 | if (vdev->v4l2_dev && vdev->v4l2_dev->mdev && | ||
437 | vdev->vfl_type != VFL_TYPE_SUBDEV) | ||
438 | media_entity_put(entity); | ||
439 | #endif | ||
440 | video_put(vdev); | 427 | video_put(vdev); |
441 | } | ||
442 | return ret; | 428 | return ret; |
443 | } | 429 | } |
444 | 430 | ||
@@ -455,11 +441,6 @@ static int v4l2_release(struct inode *inode, struct file *filp) | |||
455 | if (vdev->lock) | 441 | if (vdev->lock) |
456 | mutex_unlock(vdev->lock); | 442 | mutex_unlock(vdev->lock); |
457 | } | 443 | } |
458 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
459 | if (vdev->v4l2_dev && vdev->v4l2_dev->mdev && | ||
460 | vdev->vfl_type != VFL_TYPE_SUBDEV) | ||
461 | media_entity_put(&vdev->entity); | ||
462 | #endif | ||
463 | /* decrease the refcount unconditionally since the release() | 444 | /* decrease the refcount unconditionally since the release() |
464 | return value is ignored. */ | 445 | return value is ignored. */ |
465 | video_put(vdev); | 446 | video_put(vdev); |
@@ -754,12 +735,6 @@ void video_unregister_device(struct video_device *vdev) | |||
754 | if (!vdev || !video_is_registered(vdev)) | 735 | if (!vdev || !video_is_registered(vdev)) |
755 | return; | 736 | return; |
756 | 737 | ||
757 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
758 | if (vdev->v4l2_dev && vdev->v4l2_dev->mdev && | ||
759 | vdev->vfl_type != VFL_TYPE_SUBDEV) | ||
760 | media_device_unregister_entity(&vdev->entity); | ||
761 | #endif | ||
762 | |||
763 | mutex_lock(&videodev_lock); | 738 | mutex_lock(&videodev_lock); |
764 | /* This must be in a critical section to prevent a race with v4l2_open. | 739 | /* This must be in a critical section to prevent a race with v4l2_open. |
765 | * Once this bit has been cleared video_get may never be called again. | 740 | * Once this bit has been cleared video_get may never be called again. |