Commit ee6869afc922a9849979e49bb3bbcad794872fcb

Authored by Hans Verkuil
Committed by Mauro Carvalho Chehab
1 parent c29fcff3da

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>

Showing 4 changed files with 89 additions and 19 deletions Side-by-side Diff

Documentation/video4linux/v4l2-framework.txt
... ... @@ -453,6 +453,10 @@
453 453 - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
454 454 (highly recommended to use this and it might become compulsory in the
455 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 460 - parent: you only set this if v4l2_device was registered with NULL as
457 461 the parent device struct. This only happens in cases where one hardware
458 462 device has multiple PCI devices that all share the same v4l2_device core.
... ... @@ -469,6 +473,22 @@
469 473 The v4l2_file_operations struct is a subset of file_operations. The main
470 474 difference is that the inode argument is omitted since it is never used.
471 475  
  476 +v4l2_file_operations and locking
  477 +--------------------------------
  478 +
  479 +You can set a pointer to a mutex_lock in struct video_device. Usually this
  480 +will be either a top-level mutex or a mutex per device node. If you want
  481 +finer-grained locking then you have to set it to NULL and do you own locking.
  482 +
  483 +If a lock is specified then all file operations will be serialized on that
  484 +lock. If you use videobuf then you must pass the same lock to the videobuf
  485 +queue initialize function: if videobuf has to wait for a frame to arrive, then
  486 +it will temporarily unlock the lock and relock it afterwards. If your driver
  487 +also waits in the code, then you should do the same to allow other processes
  488 +to access the device node while the first process is waiting for something.
  489 +
  490 +The implementation of a hotplug disconnect should also take the lock before
  491 +calling v4l2_device_disconnect and video_unregister_device.
472 492  
473 493 video_device registration
474 494 -------------------------
drivers/media/video/v4l2-dev.c
... ... @@ -187,33 +187,50 @@
187 187 size_t sz, loff_t *off)
188 188 {
189 189 struct video_device *vdev = video_devdata(filp);
  190 + int ret = -EIO;
190 191  
191 192 if (!vdev->fops->read)
192 193 return -EINVAL;
193   - if (!video_is_registered(vdev))
194   - return -EIO;
195   - return vdev->fops->read(filp, buf, sz, off);
  194 + if (vdev->lock)
  195 + mutex_lock(vdev->lock);
  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  
198 203 static ssize_t v4l2_write(struct file *filp, const char __user *buf,
199 204 size_t sz, loff_t *off)
200 205 {
201 206 struct video_device *vdev = video_devdata(filp);
  207 + int ret = -EIO;
202 208  
203 209 if (!vdev->fops->write)
204 210 return -EINVAL;
205   - if (!video_is_registered(vdev))
206   - return -EIO;
207   - return vdev->fops->write(filp, buf, sz, off);
  211 + if (vdev->lock)
  212 + mutex_lock(vdev->lock);
  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  
210 220 static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
211 221 {
212 222 struct video_device *vdev = video_devdata(filp);
  223 + int ret = DEFAULT_POLLMASK;
213 224  
214   - if (!vdev->fops->poll || !video_is_registered(vdev))
215   - return DEFAULT_POLLMASK;
216   - return vdev->fops->poll(filp, poll);
  225 + if (!vdev->fops->poll)
  226 + return ret;
  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  
219 236 static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
220 237  
... ... @@ -224,7 +241,11 @@
224 241 if (!vdev->fops->ioctl)
225 242 return -ENOTTY;
226 243 if (vdev->fops->unlocked_ioctl) {
  244 + if (vdev->lock)
  245 + mutex_lock(vdev->lock);
227 246 ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
  247 + if (vdev->lock)
  248 + mutex_unlock(vdev->lock);
228 249 } else if (vdev->fops->ioctl) {
229 250 /* TODO: convert all drivers to unlocked_ioctl */
230 251 lock_kernel();
231 252  
... ... @@ -239,10 +260,17 @@
239 260 static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
240 261 {
241 262 struct video_device *vdev = video_devdata(filp);
  263 + int ret = -ENODEV;
242 264  
243   - if (!vdev->fops->mmap || !video_is_registered(vdev))
244   - return -ENODEV;
245   - return vdev->fops->mmap(filp, vm);
  265 + if (!vdev->fops->mmap)
  266 + return ret;
  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 276 /* Override for the open function */
249 277  
... ... @@ -254,17 +282,24 @@
254 282 /* Check if the video device is available */
255 283 mutex_lock(&videodev_lock);
256 284 vdev = video_devdata(filp);
257   - /* return ENODEV if the video device has been removed
258   - already or if it is not registered anymore. */
259   - if (vdev == NULL || !video_is_registered(vdev)) {
  285 + /* return ENODEV if the video device has already been removed. */
  286 + if (vdev == NULL) {
260 287 mutex_unlock(&videodev_lock);
261 288 return -ENODEV;
262 289 }
263 290 /* and increase the device refcount */
264 291 video_get(vdev);
265 292 mutex_unlock(&videodev_lock);
266   - if (vdev->fops->open)
267   - ret = vdev->fops->open(filp);
  293 + if (vdev->fops->open) {
  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 304 /* decrease the refcount in case of an error */
270 305 if (ret)
271 306  
... ... @@ -278,8 +313,13 @@
278 313 struct video_device *vdev = video_devdata(filp);
279 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 319 vdev->fops->release(filp);
  320 + if (vdev->lock)
  321 + mutex_unlock(vdev->lock);
  322 + }
283 323  
284 324 /* decrease the refcount unconditionally since the release()
285 325 return value is ignored. */
drivers/media/video/v4l2-event.c
... ... @@ -134,14 +134,21 @@
134 134 if (nonblocking)
135 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 141 do {
138 142 ret = wait_event_interruptible(events->wait,
139 143 events->navailable != 0);
140 144 if (ret < 0)
141   - return ret;
  145 + break;
142 146  
143 147 ret = __v4l2_event_dequeue(fh, event);
144 148 } while (ret == -ENOENT);
  149 +
  150 + if (fh->vdev->lock)
  151 + mutex_lock(fh->vdev->lock);
145 152  
146 153 return ret;
147 154 }
include/media/v4l2-dev.h
... ... @@ -94,6 +94,9 @@
94 94  
95 95 /* ioctl callbacks */
96 96 const struct v4l2_ioctl_ops *ioctl_ops;
  97 +
  98 + /* serialization lock */
  99 + struct mutex *lock;
97 100 };
98 101  
99 102 /* dev to video-device */