diff options
Diffstat (limited to 'drivers/media/video/v4l2-dev.c')
-rw-r--r-- | drivers/media/video/v4l2-dev.c | 72 |
1 files changed, 53 insertions, 19 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 0ca7978654b5..359e23290a7e 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <linux/init.h> | 25 | #include <linux/init.h> |
26 | #include <linux/kmod.h> | 26 | #include <linux/kmod.h> |
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/smp_lock.h> | ||
29 | #include <asm/uaccess.h> | 28 | #include <asm/uaccess.h> |
30 | #include <asm/system.h> | 29 | #include <asm/system.h> |
31 | 30 | ||
@@ -187,12 +186,12 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, | |||
187 | size_t sz, loff_t *off) | 186 | size_t sz, loff_t *off) |
188 | { | 187 | { |
189 | struct video_device *vdev = video_devdata(filp); | 188 | struct video_device *vdev = video_devdata(filp); |
190 | int ret = -EIO; | 189 | int ret = -ENODEV; |
191 | 190 | ||
192 | if (!vdev->fops->read) | 191 | if (!vdev->fops->read) |
193 | return -EINVAL; | 192 | return -EINVAL; |
194 | if (vdev->lock) | 193 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) |
195 | mutex_lock(vdev->lock); | 194 | return -ERESTARTSYS; |
196 | if (video_is_registered(vdev)) | 195 | if (video_is_registered(vdev)) |
197 | ret = vdev->fops->read(filp, buf, sz, off); | 196 | ret = vdev->fops->read(filp, buf, sz, off); |
198 | if (vdev->lock) | 197 | if (vdev->lock) |
@@ -204,12 +203,12 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, | |||
204 | size_t sz, loff_t *off) | 203 | size_t sz, loff_t *off) |
205 | { | 204 | { |
206 | struct video_device *vdev = video_devdata(filp); | 205 | struct video_device *vdev = video_devdata(filp); |
207 | int ret = -EIO; | 206 | int ret = -ENODEV; |
208 | 207 | ||
209 | if (!vdev->fops->write) | 208 | if (!vdev->fops->write) |
210 | return -EINVAL; | 209 | return -EINVAL; |
211 | if (vdev->lock) | 210 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) |
212 | mutex_lock(vdev->lock); | 211 | return -ERESTARTSYS; |
213 | if (video_is_registered(vdev)) | 212 | if (video_is_registered(vdev)) |
214 | ret = vdev->fops->write(filp, buf, sz, off); | 213 | ret = vdev->fops->write(filp, buf, sz, off); |
215 | if (vdev->lock) | 214 | if (vdev->lock) |
@@ -220,10 +219,10 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, | |||
220 | static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) | 219 | static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) |
221 | { | 220 | { |
222 | struct video_device *vdev = video_devdata(filp); | 221 | struct video_device *vdev = video_devdata(filp); |
223 | int ret = DEFAULT_POLLMASK; | 222 | int ret = POLLERR | POLLHUP; |
224 | 223 | ||
225 | if (!vdev->fops->poll) | 224 | if (!vdev->fops->poll) |
226 | return ret; | 225 | return DEFAULT_POLLMASK; |
227 | if (vdev->lock) | 226 | if (vdev->lock) |
228 | mutex_lock(vdev->lock); | 227 | mutex_lock(vdev->lock); |
229 | if (video_is_registered(vdev)) | 228 | if (video_is_registered(vdev)) |
@@ -239,18 +238,45 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
239 | int ret = -ENODEV; | 238 | int ret = -ENODEV; |
240 | 239 | ||
241 | if (vdev->fops->unlocked_ioctl) { | 240 | if (vdev->fops->unlocked_ioctl) { |
242 | if (vdev->lock) | 241 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) |
243 | mutex_lock(vdev->lock); | 242 | return -ERESTARTSYS; |
244 | if (video_is_registered(vdev)) | 243 | if (video_is_registered(vdev)) |
245 | ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); | 244 | ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); |
246 | if (vdev->lock) | 245 | if (vdev->lock) |
247 | mutex_unlock(vdev->lock); | 246 | mutex_unlock(vdev->lock); |
248 | } else if (vdev->fops->ioctl) { | 247 | } else if (vdev->fops->ioctl) { |
249 | /* TODO: convert all drivers to unlocked_ioctl */ | 248 | /* This code path is a replacement for the BKL. It is a major |
250 | lock_kernel(); | 249 | * hack but it will have to do for those drivers that are not |
250 | * yet converted to use unlocked_ioctl. | ||
251 | * | ||
252 | * There are two options: if the driver implements struct | ||
253 | * v4l2_device, then the lock defined there is used to | ||
254 | * serialize the ioctls. Otherwise the v4l2 core lock defined | ||
255 | * below is used. This lock is really bad since it serializes | ||
256 | * completely independent devices. | ||
257 | * | ||
258 | * Both variants suffer from the same problem: if the driver | ||
259 | * sleeps, then it blocks all ioctls since the lock is still | ||
260 | * held. This is very common for VIDIOC_DQBUF since that | ||
261 | * normally waits for a frame to arrive. As a result any other | ||
262 | * ioctl calls will proceed very, very slowly since each call | ||
263 | * will have to wait for the VIDIOC_QBUF to finish. Things that | ||
264 | * should take 0.01s may now take 10-20 seconds. | ||
265 | * | ||
266 | * The workaround is to *not* take the lock for VIDIOC_DQBUF. | ||
267 | * This actually works OK for videobuf-based drivers, since | ||
268 | * videobuf will take its own internal lock. | ||
269 | */ | ||
270 | static DEFINE_MUTEX(v4l2_ioctl_mutex); | ||
271 | struct mutex *m = vdev->v4l2_dev ? | ||
272 | &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex; | ||
273 | |||
274 | if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m)) | ||
275 | return -ERESTARTSYS; | ||
251 | if (video_is_registered(vdev)) | 276 | if (video_is_registered(vdev)) |
252 | ret = vdev->fops->ioctl(filp, cmd, arg); | 277 | ret = vdev->fops->ioctl(filp, cmd, arg); |
253 | unlock_kernel(); | 278 | if (cmd != VIDIOC_DQBUF) |
279 | mutex_unlock(m); | ||
254 | } else | 280 | } else |
255 | ret = -ENOTTY; | 281 | ret = -ENOTTY; |
256 | 282 | ||
@@ -264,8 +290,8 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) | |||
264 | 290 | ||
265 | if (!vdev->fops->mmap) | 291 | if (!vdev->fops->mmap) |
266 | return ret; | 292 | return ret; |
267 | if (vdev->lock) | 293 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) |
268 | mutex_lock(vdev->lock); | 294 | return -ERESTARTSYS; |
269 | if (video_is_registered(vdev)) | 295 | if (video_is_registered(vdev)) |
270 | ret = vdev->fops->mmap(filp, vm); | 296 | ret = vdev->fops->mmap(filp, vm); |
271 | if (vdev->lock) | 297 | if (vdev->lock) |
@@ -283,7 +309,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) | |||
283 | mutex_lock(&videodev_lock); | 309 | mutex_lock(&videodev_lock); |
284 | vdev = video_devdata(filp); | 310 | vdev = video_devdata(filp); |
285 | /* return ENODEV if the video device has already been removed. */ | 311 | /* return ENODEV if the video device has already been removed. */ |
286 | if (vdev == NULL) { | 312 | if (vdev == NULL || !video_is_registered(vdev)) { |
287 | mutex_unlock(&videodev_lock); | 313 | mutex_unlock(&videodev_lock); |
288 | return -ENODEV; | 314 | return -ENODEV; |
289 | } | 315 | } |
@@ -291,8 +317,10 @@ static int v4l2_open(struct inode *inode, struct file *filp) | |||
291 | video_get(vdev); | 317 | video_get(vdev); |
292 | mutex_unlock(&videodev_lock); | 318 | mutex_unlock(&videodev_lock); |
293 | if (vdev->fops->open) { | 319 | if (vdev->fops->open) { |
294 | if (vdev->lock) | 320 | if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { |
295 | mutex_lock(vdev->lock); | 321 | ret = -ERESTARTSYS; |
322 | goto err; | ||
323 | } | ||
296 | if (video_is_registered(vdev)) | 324 | if (video_is_registered(vdev)) |
297 | ret = vdev->fops->open(filp); | 325 | ret = vdev->fops->open(filp); |
298 | else | 326 | else |
@@ -301,6 +329,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) | |||
301 | mutex_unlock(vdev->lock); | 329 | mutex_unlock(vdev->lock); |
302 | } | 330 | } |
303 | 331 | ||
332 | err: | ||
304 | /* decrease the refcount in case of an error */ | 333 | /* decrease the refcount in case of an error */ |
305 | if (ret) | 334 | if (ret) |
306 | video_put(vdev); | 335 | video_put(vdev); |
@@ -595,7 +624,12 @@ void video_unregister_device(struct video_device *vdev) | |||
595 | if (!vdev || !video_is_registered(vdev)) | 624 | if (!vdev || !video_is_registered(vdev)) |
596 | return; | 625 | return; |
597 | 626 | ||
627 | mutex_lock(&videodev_lock); | ||
628 | /* This must be in a critical section to prevent a race with v4l2_open. | ||
629 | * Once this bit has been cleared video_get may never be called again. | ||
630 | */ | ||
598 | clear_bit(V4L2_FL_REGISTERED, &vdev->flags); | 631 | clear_bit(V4L2_FL_REGISTERED, &vdev->flags); |
632 | mutex_unlock(&videodev_lock); | ||
599 | device_unregister(&vdev->dev); | 633 | device_unregister(&vdev->dev); |
600 | } | 634 | } |
601 | EXPORT_SYMBOL(video_unregister_device); | 635 | EXPORT_SYMBOL(video_unregister_device); |