aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2011-11-17 03:32:17 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-12-30 13:06:49 -0500
commitb037c0fde22b1d3cd0b3c3717d28e54619fc1592 (patch)
tree92d4c05c8e3f777c288546d5592cdf8ce4b70a89
parentf0b7c7fc6f15e823cb4a5d225d9ef28b884ab6ec (diff)
[media] media: vb2: fix potential deadlock in mmap vs. get_userptr handling
To get direct access to userspace memory pages vb2 allocator needs to gather read access on mmap semaphore in the current process. The same semaphore is taken before calling mmap operation, while both mmap and qbuf are called by the driver or v4l2 core with driver's lock held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core release driver's lock, takes mmap_sem and then takes again driver's lock. get_userptr methods are now called with all needed locks already taken to avoid further lock magic inside memory allocator's code. Reported-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> CC: Pawel Osciak <pawel@osciak.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/videobuf2-core.c51
-rw-r--r--drivers/media/video/videobuf2-dma-sg.c3
-rw-r--r--drivers/media/video/videobuf2-memops.c28
3 files changed, 53 insertions, 29 deletions
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 4d22b8214f98..a3a9bf908d84 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -1082,46 +1082,76 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf);
1082 */ 1082 */
1083int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) 1083int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
1084{ 1084{
1085 struct rw_semaphore *mmap_sem = NULL;
1085 struct vb2_buffer *vb; 1086 struct vb2_buffer *vb;
1086 int ret; 1087 int ret = 0;
1088
1089 /*
1090 * In case of user pointer buffers vb2 allocator needs to get direct
1091 * access to userspace pages. This requires getting read access on
1092 * mmap semaphore in the current process structure. The same
1093 * semaphore is taken before calling mmap operation, while both mmap
1094 * and qbuf are called by the driver or v4l2 core with driver's lock
1095 * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in
1096 * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core
1097 * release driver's lock, takes mmap_sem and then takes again driver's
1098 * lock.
1099 *
1100 * To avoid race with other vb2 calls, which might be called after
1101 * releasing driver's lock, this operation is performed at the
1102 * beggining of qbuf processing. This way the queue status is
1103 * consistent after getting driver's lock back.
1104 */
1105 if (b->type == V4L2_MEMORY_USERPTR) {
1106 mmap_sem = &current->mm->mmap_sem;
1107 call_qop(q, wait_prepare, q);
1108 down_read(mmap_sem);
1109 call_qop(q, wait_finish, q);
1110 }
1087 1111
1088 if (q->fileio) { 1112 if (q->fileio) {
1089 dprintk(1, "qbuf: file io in progress\n"); 1113 dprintk(1, "qbuf: file io in progress\n");
1090 return -EBUSY; 1114 ret = -EBUSY;
1115 goto unlock;
1091 } 1116 }
1092 1117
1093 if (b->type != q->type) { 1118 if (b->type != q->type) {
1094 dprintk(1, "qbuf: invalid buffer type\n"); 1119 dprintk(1, "qbuf: invalid buffer type\n");
1095 return -EINVAL; 1120 ret = -EINVAL;
1121 goto unlock;
1096 } 1122 }
1097 1123
1098 if (b->index >= q->num_buffers) { 1124 if (b->index >= q->num_buffers) {
1099 dprintk(1, "qbuf: buffer index out of range\n"); 1125 dprintk(1, "qbuf: buffer index out of range\n");
1100 return -EINVAL; 1126 ret = -EINVAL;
1127 goto unlock;
1101 } 1128 }
1102 1129
1103 vb = q->bufs[b->index]; 1130 vb = q->bufs[b->index];
1104 if (NULL == vb) { 1131 if (NULL == vb) {
1105 /* Should never happen */ 1132 /* Should never happen */
1106 dprintk(1, "qbuf: buffer is NULL\n"); 1133 dprintk(1, "qbuf: buffer is NULL\n");
1107 return -EINVAL; 1134 ret = -EINVAL;
1135 goto unlock;
1108 } 1136 }
1109 1137
1110 if (b->memory != q->memory) { 1138 if (b->memory != q->memory) {
1111 dprintk(1, "qbuf: invalid memory type\n"); 1139 dprintk(1, "qbuf: invalid memory type\n");
1112 return -EINVAL; 1140 ret = -EINVAL;
1141 goto unlock;
1113 } 1142 }
1114 1143
1115 switch (vb->state) { 1144 switch (vb->state) {
1116 case VB2_BUF_STATE_DEQUEUED: 1145 case VB2_BUF_STATE_DEQUEUED:
1117 ret = __buf_prepare(vb, b); 1146 ret = __buf_prepare(vb, b);
1118 if (ret) 1147 if (ret)
1119 return ret; 1148 goto unlock;
1120 case VB2_BUF_STATE_PREPARED: 1149 case VB2_BUF_STATE_PREPARED:
1121 break; 1150 break;
1122 default: 1151 default:
1123 dprintk(1, "qbuf: buffer already in use\n"); 1152 dprintk(1, "qbuf: buffer already in use\n");
1124 return -EINVAL; 1153 ret = -EINVAL;
1154 goto unlock;
1125 } 1155 }
1126 1156
1127 /* 1157 /*
@@ -1142,7 +1172,10 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
1142 __fill_v4l2_buffer(vb, b); 1172 __fill_v4l2_buffer(vb, b);
1143 1173
1144 dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); 1174 dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
1145 return 0; 1175unlock:
1176 if (mmap_sem)
1177 up_read(mmap_sem);
1178 return ret;
1146} 1179}
1147EXPORT_SYMBOL_GPL(vb2_qbuf); 1180EXPORT_SYMBOL_GPL(vb2_qbuf);
1148 1181
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
index 3bad8b105fea..25c3b360e1ad 100644
--- a/drivers/media/video/videobuf2-dma-sg.c
+++ b/drivers/media/video/videobuf2-dma-sg.c
@@ -140,7 +140,6 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
140 if (!buf->pages) 140 if (!buf->pages)
141 goto userptr_fail_pages_array_alloc; 141 goto userptr_fail_pages_array_alloc;
142 142
143 down_read(&current->mm->mmap_sem);
144 num_pages_from_user = get_user_pages(current, current->mm, 143 num_pages_from_user = get_user_pages(current, current->mm,
145 vaddr & PAGE_MASK, 144 vaddr & PAGE_MASK,
146 buf->sg_desc.num_pages, 145 buf->sg_desc.num_pages,
@@ -148,7 +147,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
148 1, /* force */ 147 1, /* force */
149 buf->pages, 148 buf->pages,
150 NULL); 149 NULL);
151 up_read(&current->mm->mmap_sem); 150
152 if (num_pages_from_user != buf->sg_desc.num_pages) 151 if (num_pages_from_user != buf->sg_desc.num_pages)
153 goto userptr_fail_get_user_pages; 152 goto userptr_fail_get_user_pages;
154 153
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
index 71a7a78c3fc0..c41cb60245d6 100644
--- a/drivers/media/video/videobuf2-memops.c
+++ b/drivers/media/video/videobuf2-memops.c
@@ -100,29 +100,26 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
100 unsigned long offset, start, end; 100 unsigned long offset, start, end;
101 unsigned long this_pfn, prev_pfn; 101 unsigned long this_pfn, prev_pfn;
102 dma_addr_t pa = 0; 102 dma_addr_t pa = 0;
103 int ret = -EFAULT;
104 103
105 start = vaddr; 104 start = vaddr;
106 offset = start & ~PAGE_MASK; 105 offset = start & ~PAGE_MASK;
107 end = start + size; 106 end = start + size;
108 107
109 down_read(&mm->mmap_sem);
110 vma = find_vma(mm, start); 108 vma = find_vma(mm, start);
111 109
112 if (vma == NULL || vma->vm_end < end) 110 if (vma == NULL || vma->vm_end < end)
113 goto done; 111 return -EFAULT;
114 112
115 for (prev_pfn = 0; start < end; start += PAGE_SIZE) { 113 for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
116 ret = follow_pfn(vma, start, &this_pfn); 114 int ret = follow_pfn(vma, start, &this_pfn);
117 if (ret) 115 if (ret)
118 goto done; 116 return ret;
119 117
120 if (prev_pfn == 0) 118 if (prev_pfn == 0)
121 pa = this_pfn << PAGE_SHIFT; 119 pa = this_pfn << PAGE_SHIFT;
122 else if (this_pfn != prev_pfn + 1) { 120 else if (this_pfn != prev_pfn + 1)
123 ret = -EFAULT; 121 return -EFAULT;
124 goto done; 122
125 }
126 prev_pfn = this_pfn; 123 prev_pfn = this_pfn;
127 } 124 }
128 125
@@ -130,16 +127,11 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
130 * Memory is contigous, lock vma and return to the caller 127 * Memory is contigous, lock vma and return to the caller
131 */ 128 */
132 *res_vma = vb2_get_vma(vma); 129 *res_vma = vb2_get_vma(vma);
133 if (*res_vma == NULL) { 130 if (*res_vma == NULL)
134 ret = -ENOMEM; 131 return -ENOMEM;
135 goto done;
136 }
137 *res_pa = pa + offset;
138 ret = 0;
139 132
140done: 133 *res_pa = pa + offset;
141 up_read(&mm->mmap_sem); 134 return 0;
142 return ret;
143} 135}
144EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); 136EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
145 137