aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2010-09-26 07:47:38 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-20 23:06:14 -0400
commitee6869afc922a9849979e49bb3bbcad794872fcb (patch)
tree2266050d01da694d04b533a6509873888327108b
parentc29fcff3daafbf46d64a543c1950bbd206ad8c1c (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>
-rw-r--r--Documentation/video4linux/v4l2-framework.txt20
-rw-r--r--drivers/media/video/v4l2-dev.c76
-rw-r--r--drivers/media/video/v4l2-event.c9
-rw-r--r--include/media/v4l2-dev.h3
4 files changed, 89 insertions, 19 deletions
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index 9b1d81c26b7d..a128e012a45c 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -453,6 +453,10 @@ You should also set these fields:
453- ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance 453- ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
454 (highly recommended to use this and it might become compulsory in the 454 (highly recommended to use this and it might become compulsory in the
455 future!), then set this to your v4l2_ioctl_ops struct. 455 future!), then set this to your v4l2_ioctl_ops struct.
456- lock: leave to NULL if you want to do all the locking in the driver.
457 Otherwise you give it a pointer to a struct mutex_lock and before any
458 of the v4l2_file_operations is called this lock will be taken by the
459 core and released afterwards.
456- parent: you only set this if v4l2_device was registered with NULL as 460- parent: you only set this if v4l2_device was registered with NULL as
457 the parent device struct. This only happens in cases where one hardware 461 the parent device struct. This only happens in cases where one hardware
458 device has multiple PCI devices that all share the same v4l2_device core. 462 device has multiple PCI devices that all share the same v4l2_device core.
@@ -469,6 +473,22 @@ If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or
469The v4l2_file_operations struct is a subset of file_operations. The main 473The v4l2_file_operations struct is a subset of file_operations. The main
470difference is that the inode argument is omitted since it is never used. 474difference is that the inode argument is omitted since it is never used.
471 475
476v4l2_file_operations and locking
477--------------------------------
478
479You can set a pointer to a mutex_lock in struct video_device. Usually this
480will be either a top-level mutex or a mutex per device node. If you want
481finer-grained locking then you have to set it to NULL and do you own locking.
482
483If a lock is specified then all file operations will be serialized on that
484lock. If you use videobuf then you must pass the same lock to the videobuf
485queue initialize function: if videobuf has to wait for a frame to arrive, then
486it will temporarily unlock the lock and relock it afterwards. If your driver
487also waits in the code, then you should do the same to allow other processes
488to access the device node while the first process is waiting for something.
489
490The implementation of a hotplug disconnect should also take the lock before
491calling v4l2_device_disconnect and video_unregister_device.
472 492
473video_device registration 493video_device registration
474------------------------- 494-------------------------
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 5a54eabd4c42..a7702e3d149c 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,
187 size_t sz, loff_t *off) 187 size_t sz, loff_t *off)
188{ 188{
189 struct video_device *vdev = video_devdata(filp); 189 struct video_device *vdev = video_devdata(filp);
190 int ret = -EIO;
190 191
191 if (!vdev->fops->read) 192 if (!vdev->fops->read)
192 return -EINVAL; 193 return -EINVAL;
193 if (!video_is_registered(vdev)) 194 if (vdev->lock)
194 return -EIO; 195 mutex_lock(vdev->lock);
195 return vdev->fops->read(filp, buf, sz, off); 196 if (video_is_registered(vdev))
197 ret = vdev->fops->read(filp, buf, sz, off);
198 if (vdev->lock)
199 mutex_unlock(vdev->lock);
200 return ret;
196} 201}
197 202
198static ssize_t v4l2_write(struct file *filp, const char __user *buf, 203static ssize_t v4l2_write(struct file *filp, const char __user *buf,
199 size_t sz, loff_t *off) 204 size_t sz, loff_t *off)
200{ 205{
201 struct video_device *vdev = video_devdata(filp); 206 struct video_device *vdev = video_devdata(filp);
207 int ret = -EIO;
202 208
203 if (!vdev->fops->write) 209 if (!vdev->fops->write)
204 return -EINVAL; 210 return -EINVAL;
205 if (!video_is_registered(vdev)) 211 if (vdev->lock)
206 return -EIO; 212 mutex_lock(vdev->lock);
207 return vdev->fops->write(filp, buf, sz, off); 213 if (video_is_registered(vdev))
214 ret = vdev->fops->write(filp, buf, sz, off);
215 if (vdev->lock)
216 mutex_unlock(vdev->lock);
217 return ret;
208} 218}
209 219
210static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) 220static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
211{ 221{
212 struct video_device *vdev = video_devdata(filp); 222 struct video_device *vdev = video_devdata(filp);
223 int ret = DEFAULT_POLLMASK;
213 224
214 if (!vdev->fops->poll || !video_is_registered(vdev)) 225 if (!vdev->fops->poll)
215 return DEFAULT_POLLMASK; 226 return ret;
216 return vdev->fops->poll(filp, poll); 227 if (vdev->lock)
228 mutex_lock(vdev->lock);
229 if (video_is_registered(vdev))
230 ret = vdev->fops->poll(filp, poll);
231 if (vdev->lock)
232 mutex_unlock(vdev->lock);
233 return ret;
217} 234}
218 235
219static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 236static 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)
224 if (!vdev->fops->ioctl) 241 if (!vdev->fops->ioctl)
225 return -ENOTTY; 242 return -ENOTTY;
226 if (vdev->fops->unlocked_ioctl) { 243 if (vdev->fops->unlocked_ioctl) {
244 if (vdev->lock)
245 mutex_lock(vdev->lock);
227 ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); 246 ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
247 if (vdev->lock)
248 mutex_unlock(vdev->lock);
228 } else if (vdev->fops->ioctl) { 249 } else if (vdev->fops->ioctl) {
229 /* TODO: convert all drivers to unlocked_ioctl */ 250 /* TODO: convert all drivers to unlocked_ioctl */
230 lock_kernel(); 251 lock_kernel();
@@ -239,10 +260,17 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
239static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) 260static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
240{ 261{
241 struct video_device *vdev = video_devdata(filp); 262 struct video_device *vdev = video_devdata(filp);
263 int ret = -ENODEV;
242 264
243 if (!vdev->fops->mmap || !video_is_registered(vdev)) 265 if (!vdev->fops->mmap)
244 return -ENODEV; 266 return ret;
245 return vdev->fops->mmap(filp, vm); 267 if (vdev->lock)
268 mutex_lock(vdev->lock);
269 if (video_is_registered(vdev))
270 ret = vdev->fops->mmap(filp, vm);
271 if (vdev->lock)
272 mutex_unlock(vdev->lock);
273 return ret;
246} 274}
247 275
248/* Override for the open function */ 276/* Override for the open function */
@@ -254,17 +282,24 @@ static int v4l2_open(struct inode *inode, struct file *filp)
254 /* Check if the video device is available */ 282 /* Check if the video device is available */
255 mutex_lock(&videodev_lock); 283 mutex_lock(&videodev_lock);
256 vdev = video_devdata(filp); 284 vdev = video_devdata(filp);
257 /* return ENODEV if the video device has been removed 285 /* return ENODEV if the video device has already been removed. */
258 already or if it is not registered anymore. */ 286 if (vdev == NULL) {
259 if (vdev == NULL || !video_is_registered(vdev)) {
260 mutex_unlock(&videodev_lock); 287 mutex_unlock(&videodev_lock);
261 return -ENODEV; 288 return -ENODEV;
262 } 289 }
263 /* and increase the device refcount */ 290 /* and increase the device refcount */
264 video_get(vdev); 291 video_get(vdev);
265 mutex_unlock(&videodev_lock); 292 mutex_unlock(&videodev_lock);
266 if (vdev->fops->open) 293 if (vdev->fops->open) {
267 ret = vdev->fops->open(filp); 294 if (vdev->lock)
295 mutex_lock(vdev->lock);
296 if (video_is_registered(vdev))
297 ret = vdev->fops->open(filp);
298 else
299 ret = -ENODEV;
300 if (vdev->lock)
301 mutex_unlock(vdev->lock);
302 }
268 303
269 /* decrease the refcount in case of an error */ 304 /* decrease the refcount in case of an error */
270 if (ret) 305 if (ret)
@@ -278,8 +313,13 @@ static int v4l2_release(struct inode *inode, struct file *filp)
278 struct video_device *vdev = video_devdata(filp); 313 struct video_device *vdev = video_devdata(filp);
279 int ret = 0; 314 int ret = 0;
280 315
281 if (vdev->fops->release) 316 if (vdev->fops->release) {
317 if (vdev->lock)
318 mutex_lock(vdev->lock);
282 vdev->fops->release(filp); 319 vdev->fops->release(filp);
320 if (vdev->lock)
321 mutex_unlock(vdev->lock);
322 }
283 323
284 /* decrease the refcount unconditionally since the release() 324 /* decrease the refcount unconditionally since the release()
285 return value is ignored. */ 325 return value is ignored. */
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index de74ce07b5e2..69fd343d4774 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,
134 if (nonblocking) 134 if (nonblocking)
135 return __v4l2_event_dequeue(fh, event); 135 return __v4l2_event_dequeue(fh, event);
136 136
137 /* Release the vdev lock while waiting */
138 if (fh->vdev->lock)
139 mutex_unlock(fh->vdev->lock);
140
137 do { 141 do {
138 ret = wait_event_interruptible(events->wait, 142 ret = wait_event_interruptible(events->wait,
139 events->navailable != 0); 143 events->navailable != 0);
140 if (ret < 0) 144 if (ret < 0)
141 return ret; 145 break;
142 146
143 ret = __v4l2_event_dequeue(fh, event); 147 ret = __v4l2_event_dequeue(fh, event);
144 } while (ret == -ENOENT); 148 } while (ret == -ENOENT);
145 149
150 if (fh->vdev->lock)
151 mutex_lock(fh->vdev->lock);
152
146 return ret; 153 return ret;
147} 154}
148EXPORT_SYMBOL_GPL(v4l2_event_dequeue); 155EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index ba236ff35c8a..15802a067a12 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -94,6 +94,9 @@ struct video_device
94 94
95 /* ioctl callbacks */ 95 /* ioctl callbacks */
96 const struct v4l2_ioctl_ops *ioctl_ops; 96 const struct v4l2_ioctl_ops *ioctl_ops;
97
98 /* serialization lock */
99 struct mutex *lock;
97}; 100};
98 101
99/* dev to video-device */ 102/* dev to video-device */