[media] vb2: push the mmap semaphore down to __buf_prepare()
Rather than taking the mmap semaphore at a relatively high-level function, push it down to the place where it is really needed. It was placed in vb2_queue_or_prepare_buf() to prevent racing with other vb2 calls. The only way I can see that a race can happen is when two threads queue the same buffer. The solution for that it to introduce a PREPARING state. Moving it down offers opportunities to simplify the code. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
This commit is contained in:
		
					parent
					
						
							
								b4fcdaf765
							
						
					
				
			
			
				commit
				
					
						b18a8ff29d
					
				
			
		
					 2 changed files with 38 additions and 46 deletions
				
			
		| 
						 | 
					@ -481,6 +481,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
 | 
				
			||||||
	case VB2_BUF_STATE_PREPARED:
 | 
						case VB2_BUF_STATE_PREPARED:
 | 
				
			||||||
		b->flags |= V4L2_BUF_FLAG_PREPARED;
 | 
							b->flags |= V4L2_BUF_FLAG_PREPARED;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case VB2_BUF_STATE_PREPARING:
 | 
				
			||||||
	case VB2_BUF_STATE_DEQUEUED:
 | 
						case VB2_BUF_STATE_DEQUEUED:
 | 
				
			||||||
		/* nothing */
 | 
							/* nothing */
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -1228,6 +1229,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 | 
				
			||||||
static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 | 
					static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct vb2_queue *q = vb->vb2_queue;
 | 
						struct vb2_queue *q = vb->vb2_queue;
 | 
				
			||||||
 | 
						struct rw_semaphore *mmap_sem;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = __verify_length(vb, b);
 | 
						ret = __verify_length(vb, b);
 | 
				
			||||||
| 
						 | 
					@ -1237,12 +1239,31 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vb->state = VB2_BUF_STATE_PREPARING;
 | 
				
			||||||
	switch (q->memory) {
 | 
						switch (q->memory) {
 | 
				
			||||||
	case V4L2_MEMORY_MMAP:
 | 
						case V4L2_MEMORY_MMAP:
 | 
				
			||||||
		ret = __qbuf_mmap(vb, b);
 | 
							ret = __qbuf_mmap(vb, b);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case V4L2_MEMORY_USERPTR:
 | 
						case V4L2_MEMORY_USERPTR:
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * In case of user pointer buffers vb2 allocators need to get direct
 | 
				
			||||||
 | 
							 * access to userspace pages. This requires getting the mmap semaphore
 | 
				
			||||||
 | 
							 * for read access in the current process structure. The same semaphore
 | 
				
			||||||
 | 
							 * is taken before calling mmap operation, while both qbuf/prepare_buf
 | 
				
			||||||
 | 
							 * and mmap are called by the driver or v4l2 core with the driver's lock
 | 
				
			||||||
 | 
							 * held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap
 | 
				
			||||||
 | 
							 * and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2
 | 
				
			||||||
 | 
							 * core releases the driver's lock, takes mmap_sem and then takes the
 | 
				
			||||||
 | 
							 * driver's lock again.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							mmap_sem = ¤t->mm->mmap_sem;
 | 
				
			||||||
 | 
							call_qop(q, wait_prepare, q);
 | 
				
			||||||
 | 
							down_read(mmap_sem);
 | 
				
			||||||
 | 
							call_qop(q, wait_finish, q);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = __qbuf_userptr(vb, b);
 | 
							ret = __qbuf_userptr(vb, b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							up_read(mmap_sem);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case V4L2_MEMORY_DMABUF:
 | 
						case V4L2_MEMORY_DMABUF:
 | 
				
			||||||
		ret = __qbuf_dmabuf(vb, b);
 | 
							ret = __qbuf_dmabuf(vb, b);
 | 
				
			||||||
| 
						 | 
					@ -1256,8 +1277,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 | 
				
			||||||
		ret = call_qop(q, buf_prepare, vb);
 | 
							ret = call_qop(q, buf_prepare, vb);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
 | 
							dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
 | 
				
			||||||
	else
 | 
						vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
 | 
				
			||||||
		vb->state = VB2_BUF_STATE_PREPARED;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1268,80 +1288,47 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
 | 
				
			||||||
						   struct v4l2_buffer *,
 | 
											   struct v4l2_buffer *,
 | 
				
			||||||
						   struct vb2_buffer *))
 | 
											   struct vb2_buffer *))
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rw_semaphore *mmap_sem = NULL;
 | 
					 | 
				
			||||||
	struct vb2_buffer *vb;
 | 
						struct vb2_buffer *vb;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * In case of user pointer buffers vb2 allocators need to get direct
 | 
					 | 
				
			||||||
	 * access to userspace pages. This requires getting the mmap semaphore
 | 
					 | 
				
			||||||
	 * for read access in the current process structure. The same semaphore
 | 
					 | 
				
			||||||
	 * is taken before calling mmap operation, while both qbuf/prepare_buf
 | 
					 | 
				
			||||||
	 * and mmap are called by the driver or v4l2 core with the driver's lock
 | 
					 | 
				
			||||||
	 * held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap
 | 
					 | 
				
			||||||
	 * and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2
 | 
					 | 
				
			||||||
	 * core releases the driver's lock, takes mmap_sem and then takes the
 | 
					 | 
				
			||||||
	 * driver's lock again.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * To avoid racing with other vb2 calls, which might be called after
 | 
					 | 
				
			||||||
	 * releasing the driver's lock, this operation is performed at the
 | 
					 | 
				
			||||||
	 * beginning of qbuf/prepare_buf processing. This way the queue status
 | 
					 | 
				
			||||||
	 * is consistent after getting the driver's lock back.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (q->memory == V4L2_MEMORY_USERPTR) {
 | 
					 | 
				
			||||||
		mmap_sem = ¤t->mm->mmap_sem;
 | 
					 | 
				
			||||||
		call_qop(q, wait_prepare, q);
 | 
					 | 
				
			||||||
		down_read(mmap_sem);
 | 
					 | 
				
			||||||
		call_qop(q, wait_finish, q);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (q->fileio) {
 | 
						if (q->fileio) {
 | 
				
			||||||
		dprintk(1, "%s(): file io in progress\n", opname);
 | 
							dprintk(1, "%s(): file io in progress\n", opname);
 | 
				
			||||||
		ret = -EBUSY;
 | 
							return -EBUSY;
 | 
				
			||||||
		goto unlock;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (b->type != q->type) {
 | 
						if (b->type != q->type) {
 | 
				
			||||||
		dprintk(1, "%s(): invalid buffer type\n", opname);
 | 
							dprintk(1, "%s(): invalid buffer type\n", opname);
 | 
				
			||||||
		ret = -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
		goto unlock;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (b->index >= q->num_buffers) {
 | 
						if (b->index >= q->num_buffers) {
 | 
				
			||||||
		dprintk(1, "%s(): buffer index out of range\n", opname);
 | 
							dprintk(1, "%s(): buffer index out of range\n", opname);
 | 
				
			||||||
		ret = -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
		goto unlock;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vb = q->bufs[b->index];
 | 
						vb = q->bufs[b->index];
 | 
				
			||||||
	if (NULL == vb) {
 | 
						if (NULL == vb) {
 | 
				
			||||||
		/* Should never happen */
 | 
							/* Should never happen */
 | 
				
			||||||
		dprintk(1, "%s(): buffer is NULL\n", opname);
 | 
							dprintk(1, "%s(): buffer is NULL\n", opname);
 | 
				
			||||||
		ret = -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
		goto unlock;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (b->memory != q->memory) {
 | 
						if (b->memory != q->memory) {
 | 
				
			||||||
		dprintk(1, "%s(): invalid memory type\n", opname);
 | 
							dprintk(1, "%s(): invalid memory type\n", opname);
 | 
				
			||||||
		ret = -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
		goto unlock;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = __verify_planes_array(vb, b);
 | 
						ret = __verify_planes_array(vb, b);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto unlock;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = handler(q, b, vb);
 | 
						ret = handler(q, b, vb);
 | 
				
			||||||
	if (ret)
 | 
						if (!ret) {
 | 
				
			||||||
		goto unlock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Fill buffer information for the userspace */
 | 
							/* Fill buffer information for the userspace */
 | 
				
			||||||
		__fill_v4l2_buffer(vb, b);
 | 
							__fill_v4l2_buffer(vb, b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index);
 | 
							dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index);
 | 
				
			||||||
unlock:
 | 
						}
 | 
				
			||||||
	if (mmap_sem)
 | 
					 | 
				
			||||||
		up_read(mmap_sem);
 | 
					 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1390,6 +1377,9 @@ static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b,
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
	case VB2_BUF_STATE_PREPARED:
 | 
						case VB2_BUF_STATE_PREPARED:
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case VB2_BUF_STATE_PREPARING:
 | 
				
			||||||
 | 
							dprintk(1, "qbuf: buffer still being prepared\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		dprintk(1, "qbuf: buffer already in use\n");
 | 
							dprintk(1, "qbuf: buffer already in use\n");
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,6 +142,7 @@ enum vb2_fileio_flags {
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * enum vb2_buffer_state - current video buffer state
 | 
					 * enum vb2_buffer_state - current video buffer state
 | 
				
			||||||
 * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
 | 
					 * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
 | 
				
			||||||
 | 
					 * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf
 | 
				
			||||||
 * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver
 | 
					 * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver
 | 
				
			||||||
 * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver
 | 
					 * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver
 | 
				
			||||||
 * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
 | 
					 * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
 | 
				
			||||||
| 
						 | 
					@ -154,6 +155,7 @@ enum vb2_fileio_flags {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
enum vb2_buffer_state {
 | 
					enum vb2_buffer_state {
 | 
				
			||||||
	VB2_BUF_STATE_DEQUEUED,
 | 
						VB2_BUF_STATE_DEQUEUED,
 | 
				
			||||||
 | 
						VB2_BUF_STATE_PREPARING,
 | 
				
			||||||
	VB2_BUF_STATE_PREPARED,
 | 
						VB2_BUF_STATE_PREPARED,
 | 
				
			||||||
	VB2_BUF_STATE_QUEUED,
 | 
						VB2_BUF_STATE_QUEUED,
 | 
				
			||||||
	VB2_BUF_STATE_ACTIVE,
 | 
						VB2_BUF_STATE_ACTIVE,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue