aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorMaxim Levitsky <maximlevitsky@gmail.com>2007-09-27 19:34:09 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-10-09 23:02:58 -0400
commit9900132f3437e9373aa030cdb5bd2d5db15566e3 (patch)
treeb4b450daf16d1f502869dd472ae2bad85892daca /drivers/media
parent851c0c96b2212f48fe51afc1589541b5eae3a544 (diff)
V4L/DVB (6268): V4L: Fix a lock inversion in generic videobuf code
videobuf_qbuf takes q->lock, and then calls q->ops->buf_prepare which by design in all drivers calls videobuf_iolock which calls videobuf_dma_init_user and this takes current->mm->mmap_sem on the other hand if user calls mumap from other thread, sys_munmap takes current->mm->mmap_sem and videobuf_vm_close takes q->lock Since this can occur only for V4L2_MEMORY_MMAP buffers, take current->mm->mmap_sem in qbuf, before q->lock, and don't take current->mm->mmap_sem videobuf_dma_init_user for those buffers Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com> http://thread.gmane.org/gmane.comp.video.video4linux/34978/focus=34981 Reviewed-by: Ricardo Cerqueira <v4l@cerqueira.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/videobuf-core.c7
-rw-r--r--drivers/media/video/videobuf-dma-sg.c32
2 files changed, 34 insertions, 5 deletions
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index 3bd06bb633a7..a27e114cacef 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -349,6 +349,9 @@ int videobuf_qbuf(struct videobuf_queue *q,
349 349
350 MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS); 350 MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
351 351
352 if (b->memory == V4L2_MEMORY_MMAP)
353 down_read(&current->mm->mmap_sem);
354
352 mutex_lock(&q->lock); 355 mutex_lock(&q->lock);
353 retval = -EBUSY; 356 retval = -EBUSY;
354 if (q->reading) { 357 if (q->reading) {
@@ -434,6 +437,10 @@ int videobuf_qbuf(struct videobuf_queue *q,
434 437
435 done: 438 done:
436 mutex_unlock(&q->lock); 439 mutex_unlock(&q->lock);
440
441 if (b->memory == V4L2_MEMORY_MMAP)
442 up_read(&current->mm->mmap_sem);
443
437 return retval; 444 return retval;
438} 445}
439 446
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index 0939ede831ab..05dd38343fa3 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -134,8 +134,8 @@ void videobuf_dma_init(struct videobuf_dmabuf *dma)
134 dma->magic = MAGIC_DMABUF; 134 dma->magic = MAGIC_DMABUF;
135} 135}
136 136
137int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, 137static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
138 unsigned long data, unsigned long size) 138 int direction, unsigned long data, unsigned long size)
139{ 139{
140 unsigned long first,last; 140 unsigned long first,last;
141 int err, rw = 0; 141 int err, rw = 0;
@@ -160,12 +160,12 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
160 160
161 dma->varea = (void *) data; 161 dma->varea = (void *) data;
162 162
163 down_read(&current->mm->mmap_sem); 163
164 err = get_user_pages(current,current->mm, 164 err = get_user_pages(current,current->mm,
165 data & PAGE_MASK, dma->nr_pages, 165 data & PAGE_MASK, dma->nr_pages,
166 rw == READ, 1, /* force */ 166 rw == READ, 1, /* force */
167 dma->pages, NULL); 167 dma->pages, NULL);
168 up_read(&current->mm->mmap_sem); 168
169 if (err != dma->nr_pages) { 169 if (err != dma->nr_pages) {
170 dma->nr_pages = (err >= 0) ? err : 0; 170 dma->nr_pages = (err >= 0) ? err : 0;
171 dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages); 171 dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages);
@@ -174,6 +174,17 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
174 return 0; 174 return 0;
175} 175}
176 176
177int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
178 unsigned long data, unsigned long size)
179{
180 int ret;
181 down_read(&current->mm->mmap_sem);
182 ret = videobuf_dma_init_user_locked(dma, direction, data, size);
183 up_read(&current->mm->mmap_sem);
184
185 return ret;
186}
187
177int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, 188int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
178 int nr_pages) 189 int nr_pages)
179{ 190{
@@ -469,13 +480,24 @@ static int __videobuf_iolock (struct videobuf_queue* q,
469 pages ); 480 pages );
470 if (0 != err) 481 if (0 != err)
471 return err; 482 return err;
472 } else { 483 } else if (vb->memory == V4L2_MEMORY_USERPTR) {
473 /* dma directly to userspace */ 484 /* dma directly to userspace */
474 err = videobuf_dma_init_user( &mem->dma, 485 err = videobuf_dma_init_user( &mem->dma,
475 PCI_DMA_FROMDEVICE, 486 PCI_DMA_FROMDEVICE,
476 vb->baddr,vb->bsize ); 487 vb->baddr,vb->bsize );
477 if (0 != err) 488 if (0 != err)
478 return err; 489 return err;
490 } else {
491 /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
492 buffers can only be called from videobuf_qbuf
493 we take current->mm->mmap_sem there, to prevent
494 locking inversion, so don't take it here */
495
496 err = videobuf_dma_init_user_locked(&mem->dma,
497 PCI_DMA_FROMDEVICE,
498 vb->baddr, vb->bsize);
499 if (0 != err)
500 return err;
479 } 501 }
480 break; 502 break;
481 case V4L2_MEMORY_OVERLAY: 503 case V4L2_MEMORY_OVERLAY: