diff options
author | Hans Verkuil <hverkuil@xs4all.nl> | 2010-09-26 08:47:38 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-21 01:06:14 -0200 |
commit | ee6869afc922a9849979e49bb3bbcad794872fcb (patch) | |
tree | 2266050d01da694d04b533a6509873888327108b /drivers | |
parent | c29fcff3daafbf46d64a543c1950bbd206ad8c1c (diff) |
V4L/DVB: v4l2: add core serialization lock
Drivers can optionally set a pointer to a mutex in struct video_device.
The core will use that to lock before calling open, read, write, unlocked_ioctl,
poll, mmap or release.
Updated the documentation as well and ensure that v4l2-event knows about the
lock: it will unlock it before doing a blocking wait on an event and relock it
afterwards.
Ensure that the 'video_is_registered' check is done when the lock is held:
a typical disconnect will take the lock as well before unregistering the
device nodes, so to prevent race conditions the video_is_registered check
should also be done with the lock held.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/v4l2-dev.c | 76 | ||||
-rw-r--r-- | drivers/media/video/v4l2-event.c | 9 |
2 files changed, 66 insertions, 19 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 5a54eabd4c4..a7702e3d149 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -187,33 +187,50 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { struct video_device *vdev = video_devdata(filp); + int ret = -EIO; if (!vdev->fops->read) return -EINVAL; - if (!video_is_registered(vdev)) - return -EIO; - return vdev->fops->read(filp, buf, sz, off); + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->read(filp, buf, sz, off); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static ssize_t v4l2_write(struct file *filp, const char __user *buf, size_t sz, loff_t *off) { struct video_device *vdev = video_devdata(filp); + int ret = -EIO; if (!vdev->fops->write) return -EINVAL; - if (!video_is_registered(vdev)) - return -EIO; - return vdev->fops->write(filp, buf, sz, off); + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->write(filp, buf, sz, off); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) { struct video_device *vdev = video_devdata(filp); + int ret = DEFAULT_POLLMASK; - if (!vdev->fops->poll || !video_is_registered(vdev)) - return DEFAULT_POLLMASK; - return vdev->fops->poll(filp, poll); + if (!vdev->fops->poll) + return ret; + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->poll(filp, poll); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) @@ -224,7 +241,11 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!vdev->fops->ioctl) return -ENOTTY; if (vdev->fops->unlocked_ioctl) { + if (vdev->lock) + mutex_lock(vdev->lock); ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); + if (vdev->lock) + mutex_unlock(vdev->lock); } else if (vdev->fops->ioctl) { /* TODO: convert all drivers to unlocked_ioctl */ lock_kernel(); @@ -239,10 +260,17 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) { struct video_device *vdev = video_devdata(filp); + int ret = -ENODEV; - if (!vdev->fops->mmap || !video_is_registered(vdev)) - return -ENODEV; - return vdev->fops->mmap(filp, vm); + if (!vdev->fops->mmap) + return ret; + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->mmap(filp, vm); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } /* Override for the open function */ @@ -254,17 +282,24 @@ static int v4l2_open(struct inode *inode, struct file *filp) /* Check if the video device is available */ mutex_lock(&videodev_lock); vdev = video_devdata(filp); - /* return ENODEV if the video device has been removed - already or if it is not registered anymore. */ - if (vdev == NULL || !video_is_registered(vdev)) { + /* return ENODEV if the video device has already been removed. */ + if (vdev == NULL) { mutex_unlock(&videodev_lock); return -ENODEV; } /* and increase the device refcount */ video_get(vdev); mutex_unlock(&videodev_lock); - if (vdev->fops->open) - ret = vdev->fops->open(filp); + if (vdev->fops->open) { + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->open(filp); + else + ret = -ENODEV; + if (vdev->lock) + mutex_unlock(vdev->lock); + } /* decrease the refcount in case of an error */ if (ret) @@ -278,8 +313,13 @@ static int v4l2_release(struct inode *inode, struct file *filp) struct video_device *vdev = video_devdata(filp); int ret = 0; - if (vdev->fops->release) + if (vdev->fops->release) { + if (vdev->lock) + mutex_lock(vdev->lock); vdev->fops->release(filp); + if (vdev->lock) + mutex_unlock(vdev->lock); + } /* decrease the refcount unconditionally since the release() return value is ignored. */ diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index de74ce07b5e..69fd343d477 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -134,15 +134,22 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, if (nonblocking) return __v4l2_event_dequeue(fh, event); + /* Release the vdev lock while waiting */ + if (fh->vdev->lock) + mutex_unlock(fh->vdev->lock); + do { ret = wait_event_interruptible(events->wait, events->navailable != 0); if (ret < 0) - return ret; + break; ret = __v4l2_event_dequeue(fh, event); } while (ret == -ENOENT); + if (fh->vdev->lock) + mutex_lock(fh->vdev->lock); + return ret; } EXPORT_SYMBOL_GPL(v4l2_event_dequeue); |