diff options
author | Maxim Levitsky <maximlevitsky@gmail.com> | 2007-09-27 19:34:09 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-10-09 23:02:58 -0400 |
commit | 9900132f3437e9373aa030cdb5bd2d5db15566e3 (patch) | |
tree | b4b450daf16d1f502869dd472ae2bad85892daca /drivers/media | |
parent | 851c0c96b2212f48fe51afc1589541b5eae3a544 (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.c | 7 | ||||
-rw-r--r-- | drivers/media/video/videobuf-dma-sg.c | 32 |
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(¤t->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(¤t->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 | ||
137 | int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, | 137 | static 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(¤t->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(¤t->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 | ||
177 | int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, | ||
178 | unsigned long data, unsigned long size) | ||
179 | { | ||
180 | int ret; | ||
181 | down_read(¤t->mm->mmap_sem); | ||
182 | ret = videobuf_dma_init_user_locked(dma, direction, data, size); | ||
183 | up_read(¤t->mm->mmap_sem); | ||
184 | |||
185 | return ret; | ||
186 | } | ||
187 | |||
177 | int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, | 188 | int 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: |