diff options
author | Brandon Philips <brandon@ifup.org> | 2007-11-13 18:05:38 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-12-11 15:08:08 -0500 |
commit | 19bc5133dae9562e8824ef101464061f9854c1d8 (patch) | |
tree | 8395e2da25f3cf5291e24f972d31a215ddf421a5 /drivers/media | |
parent | 63337dd3f5506628e4831b08e39e09d7f1407769 (diff) |
V4L/DVB (6601): V4L: videobuf-core locking fixes and comments
- Add comments to functions that require that caller hold q->lock
- Add __videobuf_mmap_free that doesn't hold q->lock for use within videobuf
- Add locking to videobuf_mmap_free
- Fix linux/drivers/media/common/saa7146_video.c which was holding lock around
videobuf_read_stop
- Add locking to functions that operate on a queue
- Add videobuf_stop to take care of stopping in both the read and stream case
TODO: bttv still has an unsafe call to videobuf_queue_is_busy
Signed-off-by: Brandon Philips <bphilips@suse.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/common/saa7146_video.c | 5 | ||||
-rw-r--r-- | drivers/media/video/videobuf-core.c | 247 |
2 files changed, 159 insertions, 93 deletions
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index f245a3b2ef47..7cc4213ba56b 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c | |||
@@ -1440,10 +1440,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file) | |||
1440 | err = saa7146_stop_preview(fh); | 1440 | err = saa7146_stop_preview(fh); |
1441 | } | 1441 | } |
1442 | 1442 | ||
1443 | // release all capture buffers | 1443 | videobuf_stop(q); |
1444 | mutex_lock(&q->lock); | ||
1445 | videobuf_read_stop(q); | ||
1446 | mutex_unlock(&q->lock); | ||
1447 | 1444 | ||
1448 | /* hmm, why is this function declared void? */ | 1445 | /* hmm, why is this function declared void? */ |
1449 | /* return err */ | 1446 | /* return err */ |
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 89a44f16f0ba..de2f56b19163 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c | |||
@@ -141,6 +141,7 @@ void videobuf_queue_core_init(struct videobuf_queue* q, | |||
141 | INIT_LIST_HEAD(&q->stream); | 141 | INIT_LIST_HEAD(&q->stream); |
142 | } | 142 | } |
143 | 143 | ||
144 | /* Locking: Only usage in bttv unsafe find way to remove */ | ||
144 | int videobuf_queue_is_busy(struct videobuf_queue *q) | 145 | int videobuf_queue_is_busy(struct videobuf_queue *q) |
145 | { | 146 | { |
146 | int i; | 147 | int i; |
@@ -178,6 +179,7 @@ int videobuf_queue_is_busy(struct videobuf_queue *q) | |||
178 | return 0; | 179 | return 0; |
179 | } | 180 | } |
180 | 181 | ||
182 | /* Locking: Caller holds q->lock */ | ||
181 | void videobuf_queue_cancel(struct videobuf_queue *q) | 183 | void videobuf_queue_cancel(struct videobuf_queue *q) |
182 | { | 184 | { |
183 | unsigned long flags=0; | 185 | unsigned long flags=0; |
@@ -208,6 +210,7 @@ void videobuf_queue_cancel(struct videobuf_queue *q) | |||
208 | 210 | ||
209 | /* --------------------------------------------------------------------- */ | 211 | /* --------------------------------------------------------------------- */ |
210 | 212 | ||
213 | /* Locking: Caller holds q->lock */ | ||
211 | enum v4l2_field videobuf_next_field(struct videobuf_queue *q) | 214 | enum v4l2_field videobuf_next_field(struct videobuf_queue *q) |
212 | { | 215 | { |
213 | enum v4l2_field field = q->field; | 216 | enum v4l2_field field = q->field; |
@@ -226,6 +229,7 @@ enum v4l2_field videobuf_next_field(struct videobuf_queue *q) | |||
226 | return field; | 229 | return field; |
227 | } | 230 | } |
228 | 231 | ||
232 | /* Locking: Caller holds q->lock */ | ||
229 | static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, | 233 | static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, |
230 | struct videobuf_buffer *vb, enum v4l2_buf_type type) | 234 | struct videobuf_buffer *vb, enum v4l2_buf_type type) |
231 | { | 235 | { |
@@ -281,20 +285,108 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, | |||
281 | b->sequence = vb->field_count >> 1; | 285 | b->sequence = vb->field_count >> 1; |
282 | } | 286 | } |
283 | 287 | ||
288 | /* Locking: Caller holds q->lock */ | ||
289 | static int __videobuf_mmap_free(struct videobuf_queue *q) | ||
290 | { | ||
291 | int i; | ||
292 | int rc; | ||
293 | |||
294 | if (!q) | ||
295 | return 0; | ||
296 | |||
297 | MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS); | ||
298 | |||
299 | rc = CALL(q,mmap_free,q); | ||
300 | if (rc<0) | ||
301 | return rc; | ||
302 | |||
303 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | ||
304 | if (NULL == q->bufs[i]) | ||
305 | continue; | ||
306 | q->ops->buf_release(q,q->bufs[i]); | ||
307 | kfree(q->bufs[i]); | ||
308 | q->bufs[i] = NULL; | ||
309 | } | ||
310 | |||
311 | return rc; | ||
312 | } | ||
313 | |||
314 | int videobuf_mmap_free(struct videobuf_queue *q) | ||
315 | { | ||
316 | int ret; | ||
317 | mutex_lock(&q->lock); | ||
318 | ret = __videobuf_mmap_free(q); | ||
319 | mutex_unlock(&q->lock); | ||
320 | return ret; | ||
321 | } | ||
322 | |||
323 | /* Locking: Caller holds q->lock */ | ||
324 | static int __videobuf_mmap_setup(struct videobuf_queue *q, | ||
325 | unsigned int bcount, unsigned int bsize, | ||
326 | enum v4l2_memory memory) | ||
327 | { | ||
328 | unsigned int i; | ||
329 | int err; | ||
330 | |||
331 | MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS); | ||
332 | |||
333 | err = __videobuf_mmap_free(q); | ||
334 | if (0 != err) | ||
335 | return err; | ||
336 | |||
337 | /* Allocate and initialize buffers */ | ||
338 | for (i = 0; i < bcount; i++) { | ||
339 | q->bufs[i] = videobuf_alloc(q); | ||
340 | |||
341 | if (q->bufs[i] == NULL) | ||
342 | break; | ||
343 | |||
344 | q->bufs[i]->i = i; | ||
345 | q->bufs[i]->input = UNSET; | ||
346 | q->bufs[i]->memory = memory; | ||
347 | q->bufs[i]->bsize = bsize; | ||
348 | switch (memory) { | ||
349 | case V4L2_MEMORY_MMAP: | ||
350 | q->bufs[i]->boff = bsize * i; | ||
351 | break; | ||
352 | case V4L2_MEMORY_USERPTR: | ||
353 | case V4L2_MEMORY_OVERLAY: | ||
354 | /* nothing */ | ||
355 | break; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | if (!i) | ||
360 | return -ENOMEM; | ||
361 | |||
362 | dprintk(1,"mmap setup: %d buffers, %d bytes each\n", | ||
363 | i, bsize); | ||
364 | |||
365 | return i; | ||
366 | } | ||
367 | |||
368 | int videobuf_mmap_setup(struct videobuf_queue *q, | ||
369 | unsigned int bcount, unsigned int bsize, | ||
370 | enum v4l2_memory memory) | ||
371 | { | ||
372 | int ret; | ||
373 | mutex_lock(&q->lock); | ||
374 | ret = __videobuf_mmap_setup(q, bcount, bsize, memory); | ||
375 | mutex_unlock(&q->lock); | ||
376 | return ret; | ||
377 | } | ||
378 | |||
284 | int videobuf_reqbufs(struct videobuf_queue *q, | 379 | int videobuf_reqbufs(struct videobuf_queue *q, |
285 | struct v4l2_requestbuffers *req) | 380 | struct v4l2_requestbuffers *req) |
286 | { | 381 | { |
287 | unsigned int size,count; | 382 | unsigned int size,count; |
288 | int retval; | 383 | int retval; |
289 | 384 | ||
290 | if (req->type != q->type) { | ||
291 | dprintk(1,"reqbufs: queue type invalid\n"); | ||
292 | return -EINVAL; | ||
293 | } | ||
294 | if (req->count < 1) { | 385 | if (req->count < 1) { |
295 | dprintk(1,"reqbufs: count invalid (%d)\n",req->count); | 386 | dprintk(1,"reqbufs: count invalid (%d)\n",req->count); |
296 | return -EINVAL; | 387 | return -EINVAL; |
297 | } | 388 | } |
389 | |||
298 | if (req->memory != V4L2_MEMORY_MMAP && | 390 | if (req->memory != V4L2_MEMORY_MMAP && |
299 | req->memory != V4L2_MEMORY_USERPTR && | 391 | req->memory != V4L2_MEMORY_USERPTR && |
300 | req->memory != V4L2_MEMORY_OVERLAY) { | 392 | req->memory != V4L2_MEMORY_OVERLAY) { |
@@ -303,6 +395,12 @@ int videobuf_reqbufs(struct videobuf_queue *q, | |||
303 | } | 395 | } |
304 | 396 | ||
305 | mutex_lock(&q->lock); | 397 | mutex_lock(&q->lock); |
398 | if (req->type != q->type) { | ||
399 | dprintk(1,"reqbufs: queue type invalid\n"); | ||
400 | retval = -EINVAL; | ||
401 | goto done; | ||
402 | } | ||
403 | |||
306 | if (q->streaming) { | 404 | if (q->streaming) { |
307 | dprintk(1,"reqbufs: streaming already exists\n"); | 405 | dprintk(1,"reqbufs: streaming already exists\n"); |
308 | retval = -EBUSY; | 406 | retval = -EBUSY; |
@@ -323,7 +421,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, | |||
323 | dprintk(1,"reqbufs: bufs=%d, size=0x%x [%d pages total]\n", | 421 | dprintk(1,"reqbufs: bufs=%d, size=0x%x [%d pages total]\n", |
324 | count, size, (count*size)>>PAGE_SHIFT); | 422 | count, size, (count*size)>>PAGE_SHIFT); |
325 | 423 | ||
326 | retval = videobuf_mmap_setup(q,count,size,req->memory); | 424 | retval = __videobuf_mmap_setup(q,count,size,req->memory); |
327 | if (retval < 0) { | 425 | if (retval < 0) { |
328 | dprintk(1,"reqbufs: mmap setup returned %d\n",retval); | 426 | dprintk(1,"reqbufs: mmap setup returned %d\n",retval); |
329 | goto done; | 427 | goto done; |
@@ -338,20 +436,28 @@ int videobuf_reqbufs(struct videobuf_queue *q, | |||
338 | 436 | ||
339 | int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) | 437 | int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) |
340 | { | 438 | { |
439 | int ret = -EINVAL; | ||
440 | |||
441 | mutex_lock(&q->lock); | ||
341 | if (unlikely(b->type != q->type)) { | 442 | if (unlikely(b->type != q->type)) { |
342 | dprintk(1,"querybuf: Wrong type.\n"); | 443 | dprintk(1,"querybuf: Wrong type.\n"); |
343 | return -EINVAL; | 444 | goto done; |
344 | } | 445 | } |
345 | if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) { | 446 | if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) { |
346 | dprintk(1,"querybuf: index out of range.\n"); | 447 | dprintk(1,"querybuf: index out of range.\n"); |
347 | return -EINVAL; | 448 | goto done; |
348 | } | 449 | } |
349 | if (unlikely(NULL == q->bufs[b->index])) { | 450 | if (unlikely(NULL == q->bufs[b->index])) { |
350 | dprintk(1,"querybuf: buffer is null.\n"); | 451 | dprintk(1,"querybuf: buffer is null.\n"); |
351 | return -EINVAL; | 452 | goto done; |
352 | } | 453 | } |
454 | |||
353 | videobuf_status(q,b,q->bufs[b->index],q->type); | 455 | videobuf_status(q,b,q->bufs[b->index],q->type); |
354 | return 0; | 456 | |
457 | ret = 0; | ||
458 | done: | ||
459 | mutex_unlock(&q->lock); | ||
460 | return ret; | ||
355 | } | 461 | } |
356 | 462 | ||
357 | int videobuf_qbuf(struct videobuf_queue *q, | 463 | int videobuf_qbuf(struct videobuf_queue *q, |
@@ -541,22 +647,30 @@ int videobuf_streamon(struct videobuf_queue *q) | |||
541 | return retval; | 647 | return retval; |
542 | } | 648 | } |
543 | 649 | ||
544 | int videobuf_streamoff(struct videobuf_queue *q) | 650 | /* Locking: Caller holds q->lock */ |
651 | static int __videobuf_streamoff(struct videobuf_queue *q) | ||
545 | { | 652 | { |
546 | int retval = -EINVAL; | ||
547 | |||
548 | mutex_lock(&q->lock); | ||
549 | if (!q->streaming) | 653 | if (!q->streaming) |
550 | goto done; | 654 | return -EINVAL; |
655 | |||
551 | videobuf_queue_cancel(q); | 656 | videobuf_queue_cancel(q); |
552 | q->streaming = 0; | 657 | q->streaming = 0; |
553 | retval = 0; | ||
554 | 658 | ||
555 | done: | 659 | return 0; |
660 | } | ||
661 | |||
662 | int videobuf_streamoff(struct videobuf_queue *q) | ||
663 | { | ||
664 | int retval; | ||
665 | |||
666 | mutex_lock(&q->lock); | ||
667 | retval = __videobuf_streamoff(q); | ||
556 | mutex_unlock(&q->lock); | 668 | mutex_unlock(&q->lock); |
669 | |||
557 | return retval; | 670 | return retval; |
558 | } | 671 | } |
559 | 672 | ||
673 | /* Locking: Caller holds q->lock */ | ||
560 | static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, | 674 | static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, |
561 | char __user *data, | 675 | char __user *data, |
562 | size_t count, loff_t *ppos) | 676 | size_t count, loff_t *ppos) |
@@ -691,6 +805,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, | |||
691 | return retval; | 805 | return retval; |
692 | } | 806 | } |
693 | 807 | ||
808 | /* Locking: Caller holds q->lock */ | ||
694 | int videobuf_read_start(struct videobuf_queue *q) | 809 | int videobuf_read_start(struct videobuf_queue *q) |
695 | { | 810 | { |
696 | enum v4l2_field field; | 811 | enum v4l2_field field; |
@@ -705,7 +820,7 @@ int videobuf_read_start(struct videobuf_queue *q) | |||
705 | count = VIDEO_MAX_FRAME; | 820 | count = VIDEO_MAX_FRAME; |
706 | size = PAGE_ALIGN(size); | 821 | size = PAGE_ALIGN(size); |
707 | 822 | ||
708 | err = videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR); | 823 | err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR); |
709 | if (err < 0) | 824 | if (err < 0) |
710 | return err; | 825 | return err; |
711 | 826 | ||
@@ -728,12 +843,13 @@ int videobuf_read_start(struct videobuf_queue *q) | |||
728 | return 0; | 843 | return 0; |
729 | } | 844 | } |
730 | 845 | ||
731 | void videobuf_read_stop(struct videobuf_queue *q) | 846 | static void __videobuf_read_stop(struct videobuf_queue *q) |
732 | { | 847 | { |
733 | int i; | 848 | int i; |
734 | 849 | ||
850 | |||
735 | videobuf_queue_cancel(q); | 851 | videobuf_queue_cancel(q); |
736 | videobuf_mmap_free(q); | 852 | __videobuf_mmap_free(q); |
737 | INIT_LIST_HEAD(&q->stream); | 853 | INIT_LIST_HEAD(&q->stream); |
738 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | 854 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
739 | if (NULL == q->bufs[i]) | 855 | if (NULL == q->bufs[i]) |
@@ -743,8 +859,30 @@ void videobuf_read_stop(struct videobuf_queue *q) | |||
743 | } | 859 | } |
744 | q->read_buf = NULL; | 860 | q->read_buf = NULL; |
745 | q->reading = 0; | 861 | q->reading = 0; |
862 | |||
863 | } | ||
864 | |||
865 | void videobuf_read_stop(struct videobuf_queue *q) | ||
866 | { | ||
867 | mutex_lock(&q->lock); | ||
868 | __videobuf_read_stop(q); | ||
869 | mutex_unlock(&q->lock); | ||
870 | } | ||
871 | |||
872 | void videobuf_stop(struct videobuf_queue *q) | ||
873 | { | ||
874 | mutex_lock(&q->lock); | ||
875 | |||
876 | if (q->streaming) | ||
877 | __videobuf_streamoff(q); | ||
878 | |||
879 | if (q->reading) | ||
880 | __videobuf_read_stop(q); | ||
881 | |||
882 | mutex_unlock(&q->lock); | ||
746 | } | 883 | } |
747 | 884 | ||
885 | |||
748 | ssize_t videobuf_read_stream(struct videobuf_queue *q, | 886 | ssize_t videobuf_read_stream(struct videobuf_queue *q, |
749 | char __user *data, size_t count, loff_t *ppos, | 887 | char __user *data, size_t count, loff_t *ppos, |
750 | int vbihack, int nonblocking) | 888 | int vbihack, int nonblocking) |
@@ -858,75 +996,6 @@ unsigned int videobuf_poll_stream(struct file *file, | |||
858 | return rc; | 996 | return rc; |
859 | } | 997 | } |
860 | 998 | ||
861 | int videobuf_mmap_setup(struct videobuf_queue *q, | ||
862 | unsigned int bcount, unsigned int bsize, | ||
863 | enum v4l2_memory memory) | ||
864 | { | ||
865 | unsigned int i; | ||
866 | int err; | ||
867 | |||
868 | MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS); | ||
869 | |||
870 | err = videobuf_mmap_free(q); | ||
871 | if (0 != err) | ||
872 | return err; | ||
873 | |||
874 | /* Allocate and initialize buffers */ | ||
875 | for (i = 0; i < bcount; i++) { | ||
876 | q->bufs[i] = videobuf_alloc(q); | ||
877 | |||
878 | if (q->bufs[i] == NULL) | ||
879 | break; | ||
880 | |||
881 | q->bufs[i]->i = i; | ||
882 | q->bufs[i]->input = UNSET; | ||
883 | q->bufs[i]->memory = memory; | ||
884 | q->bufs[i]->bsize = bsize; | ||
885 | switch (memory) { | ||
886 | case V4L2_MEMORY_MMAP: | ||
887 | q->bufs[i]->boff = bsize * i; | ||
888 | break; | ||
889 | case V4L2_MEMORY_USERPTR: | ||
890 | case V4L2_MEMORY_OVERLAY: | ||
891 | /* nothing */ | ||
892 | break; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | if (!i) | ||
897 | return -ENOMEM; | ||
898 | |||
899 | dprintk(1,"mmap setup: %d buffers, %d bytes each\n", | ||
900 | i, bsize); | ||
901 | |||
902 | return i; | ||
903 | } | ||
904 | |||
905 | int videobuf_mmap_free(struct videobuf_queue *q) | ||
906 | { | ||
907 | int i; | ||
908 | int rc; | ||
909 | |||
910 | if (!q) | ||
911 | return 0; | ||
912 | |||
913 | MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS); | ||
914 | |||
915 | rc = CALL(q,mmap_free,q); | ||
916 | if (rc<0) | ||
917 | return rc; | ||
918 | |||
919 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | ||
920 | if (NULL == q->bufs[i]) | ||
921 | continue; | ||
922 | q->ops->buf_release(q,q->bufs[i]); | ||
923 | kfree(q->bufs[i]); | ||
924 | q->bufs[i] = NULL; | ||
925 | } | ||
926 | |||
927 | return rc; | ||
928 | } | ||
929 | |||
930 | int videobuf_mmap_mapper(struct videobuf_queue *q, | 999 | int videobuf_mmap_mapper(struct videobuf_queue *q, |
931 | struct vm_area_struct *vma) | 1000 | struct vm_area_struct *vma) |
932 | { | 1001 | { |
@@ -989,8 +1058,8 @@ EXPORT_SYMBOL_GPL(videobuf_dqbuf); | |||
989 | EXPORT_SYMBOL_GPL(videobuf_streamon); | 1058 | EXPORT_SYMBOL_GPL(videobuf_streamon); |
990 | EXPORT_SYMBOL_GPL(videobuf_streamoff); | 1059 | EXPORT_SYMBOL_GPL(videobuf_streamoff); |
991 | 1060 | ||
992 | EXPORT_SYMBOL_GPL(videobuf_read_start); | ||
993 | EXPORT_SYMBOL_GPL(videobuf_read_stop); | 1061 | EXPORT_SYMBOL_GPL(videobuf_read_stop); |
1062 | EXPORT_SYMBOL_GPL(videobuf_stop); | ||
994 | EXPORT_SYMBOL_GPL(videobuf_read_stream); | 1063 | EXPORT_SYMBOL_GPL(videobuf_read_stream); |
995 | EXPORT_SYMBOL_GPL(videobuf_read_one); | 1064 | EXPORT_SYMBOL_GPL(videobuf_read_one); |
996 | EXPORT_SYMBOL_GPL(videobuf_poll_stream); | 1065 | EXPORT_SYMBOL_GPL(videobuf_poll_stream); |