Re: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Marek,

On Thursday 25 November 2010 10:48:39 Marek Szyprowski wrote:
> On Thursday, November 25, 2010 2:17 AM Laurent Pinchart wrote:
> > On Friday 19 November 2010 16:55:38 Marek Szyprowski wrote:
> > > From: Pawel Osciak <p.osciak@xxxxxxxxxxx>
> > > 
> > > Videobuf2 is a Video for Linux 2 API-compatible driver framework for
> > > multimedia devices. It acts as an intermediate layer between userspace
> > > applications and device drivers. It also provides low-level, modular
> > > memory management functions for drivers.
> > > 
> > > Videobuf2 eases driver development, reduces drivers' code size and aids
> > > in proper and consistent implementation of V4L2 API in drivers.
> > > 
> > > Videobuf2 memory management backend is fully modular. This allows
> > > custom memory management routines for devices and platforms with
> > > non-standard memory management requirements to be plugged in, without
> > > changing the high-level buffer management functions and API.
> > > 
> > > The framework provides:
> > > - implementations of streaming I/O V4L2 ioctls and file operations
> > > - high-level video buffer, video queue and state management functions
> > > - video buffer memory allocation and management
> > 
> > Excellent job. I'm really pleased by the outstanding code quality.
> > Nevertheless I got a bunch of comments (I wonder if I'm known as an
> > annoying nit-picking reviewer in the community :-)).
> > 
> > I've reviewed the patch from bottom to top (starting at the header file),
> > so comments at the bottom can also apply to functions at the top when I
> > got tired repeating the same information. Sorry about that weird order.
> > 
> > Feel free to disagree with my comments, I've probably been overzealous
> > during the review to make sure everything I had a doubt about would be
> > properly addressed.
> > 
> > It's now past 2AM and I don't have enough courage to review the other
> > patches. I'm afraid they will have to wait until tomorrow. If they're of
> > the same quality as this one I'm sure they will be good.
> 
> Thanks for your hard work! I can imagine how much time you had to spend on
> catching all these things.
> 
> > BTW, where's the patch for Documentation/video4linux ? :-)
> 
> Well, I hope it will be ready one day ;) What kind of documentation do you
> expect there? Vb2 structures and functions are already documented directly
> in the source.

An explanation of what to do (and perhaps even more importantly what *not* to 
do) to use VB2. Look at Hans' documentation for the control framework for 
instance, there's an easy step-by-step explanation at the beginning. That 
really helps getting things right.

> > One last comment, why was the decision to remove all locking from vb2
> > taken ?
> 
> The idea came from Hans after posting version 1 and I really like it. It
> simplifies a lot of things in the drivers. The original idea of
> irq_spinlock and vb_lock was horrible. Having more than one queue in the
> driver meant that you need to make a lot of nasty hacks to ensure proper
> locking, because in some cases you had to take locks from all queues. The
> irq_lock usage was even worse. Videobuf used it to silently mess around
> the buffers that have been already queued to the hardware. Now, after Hans
> changes to v4l2 core (BKL removal in favor of simple mutex) almost all
> this mess can be simply removed. Proper locking can be easily ensured by
> the new mutex in the v4l2 core. There is no need to have any locks inside
> vb2. By removing irq_lock the design of the drivers is easier to
> understand, because there is no implicit actions on buffers that are
> processed by the hardware.

I agree about the irq_spinlock. The vb_lock, however, is only used to protect 
internal queue information, so I'm not sure why it would be a problem even for 
drivers that have multiple queues.

> > > +/**
> > > + * __vb2_buf_userptr_put() - release userspace memory associated
> > > associated + * with a USERPTR buffer
> > > + */
> > > +static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
> > > +{
> > > +	struct vb2_queue *q = vb->vb2_queue;
> > > +	unsigned int plane;
> > > +
> > > +	for (plane = 0; plane < vb->num_planes; ++plane) {
> > > +		call_memop(q, plane, put_userptr, vb->planes[plane].mem_priv);
> > > +		vb->planes[plane].mem_priv = NULL;
> > 
> > Any reason to initialize mem_priv to NULL here but not in
> > __vb2_buf_mem_free ?
> 
> Yes, __vb2_buf_userptr_put is called when user calls QBUF with different
> address than in the previous call, so the buffer is reinitialized without
> a call to __vb2_buf_mem_free.

That's not what I meant. You initialize mem_priv to NULL here, but you don't 
initialize it to NULL in __vb2_buf_mem_free. Shouldn't it be done there too ?

<snip>

> > > +/**
> > > + * __vb2_queue_free() - free the queue - video memory and related
> > > information + * and return the queue to an uninitialized state
> > > + */
> > > +static int __vb2_queue_free(struct vb2_queue *q)
> > > +{
> > > +	unsigned int buffer;
> > > +
> > > +	/* Call driver-provided cleanup function for each buffer, if 
provided
> > > */ +	if (q->ops->buf_cleanup) {
> > > +		for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> > > +			if (NULL == q->bufs[buffer])
> > > +				continue;
> > > +			q->ops->buf_cleanup(q->bufs[buffer]);
> > > +		}
> > > +	}
> > > +
> > > +	/* Release video buffer memory */
> > > +	__vb2_free_mem(q);
> > 
> > This is the only call to __vb2_free_mem. Maybe you could combine both
> > functions and use a single loop instead of 3 separate ones.
> 
> IMHO having 2 separate functions makes the design easier to understand. A
> single loop will be just an over-optimization as there is no performance
> issue in current version.

I could accept that, but don't try to justify inefficient code by a fear of 
over-optimization :-)

> > > +/**
> > > + * vb2_plane_cookie() - Return allocator specific cookie for the given
> > > plane + * @vb:		vb2_buffer to which the plane in question belongs to
> > > + * @plane_no:	plane number for which the address is to be returned
> > > + *
> > > + * This function returns an allocator specific cookie for a given
> > > plane if + * available, NULL otherwise. The allocator should provide
> > > some simple static + * inline function which converts this cookie to
> > > the allocator specific type + * that can be used directly by the
> > > driver to access the buffer. This can be + * for example physical
> > > address, pointer to scatter list or iommu mapping. + */
> > > +void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
> > > +{
> > > +	struct vb2_queue *q = vb->vb2_queue;
> > > +
> > > +	if (plane_no > vb->num_planes)
> > > +		return NULL;
> > > +
> > > +	return call_memop(q, plane_no, cookie,
> > > vb->planes[plane_no].mem_priv); +}
> > > +EXPORT_SYMBOL_GPL(vb2_plane_cookie);
> > 
> > If this function is meant to be called only from allocator-specific
> > functions that cast the return value to an allocator-specific type,
> > can't the allocator just calls to itself directly without going through
> > the vb2 core ?
> 
> This function will be called from allocator specific inline, which in turn
> will be called by the driver. The problem is the fact that the driver will
> have only a pointer to vb2_buffer, not the allocator context associated
> with that buffer. I don't want to mess with allocator data directly from
> the driver, so this function is really needed.

What I meant is that the allocator specific inline will call this function, 
which will in turn call a function of the same allocator. Can't the allocator 
inline function call to itself directly ?

> > > +/**
> > > + * __vb2_wait_for_done_vb() - wait for a buffer to become available
> > > + * for dequeuing
> > > + *
> > > + * Will sleep if required for nonblocking == false.
> > > + */
> > > +static int __vb2_wait_for_done_vb(struct vb2_queue *q, int
> > > nonblocking) +{
> > 
> > > +checks:
> > Labels for cleanup after errors are OK, labels for other purposes should
> > really be avoided where possible. Is there no simple way to write the
> > function without using labels ?
> 
> Most if labels are probably a left-overs from removing the locks
> 
> > > +	if (!q->streaming) {
> > > +		dprintk(1, "Streaming off, will not wait for buffers\n");
> > > +		return -EINVAL;
> > > +	}
> > 
> > This means userspace applications won't be able to dequeue remaining
> > buffers after stream off. Isn't it a serious restriction ?
> 
> Well, such restriction is written directly in the V4l2 spec: "The
> VIDIOC_STREAMOFF ioctl, apart of aborting or finishing any DMA in
> progress, unlocks any user pointer buffers locked in physical memory, and
> it removes all buffers from the incoming and outgoing queues. That means
> all images captured but not dequeued yet will be lost, likewise all images
> enqueued for output but not transmitted yet."

I can see this being a potential problem for some applications. We would need 
to change the spec to solve it though, so I'm fine with the implementation.

> > > +/**
> > > + * vb2_has_consumers() - return true if the userspace is waiting for a
> > > buffer + * @q:		videobuf2 queue
> > > + *
> > > + * This function returns true if a userspace application is waiting
> > > for a buffer + * to be ready to dequeue (on which a hardware operation
> > > has been finished). + */
> > > +bool vb2_has_consumers(struct vb2_queue *q)
> > > +{
> > > +	return waitqueue_active(&q->done_wq);
> > > +}
> > > +EXPORT_SYMBOL_GPL(vb2_has_consumers);
> > 
> > What is this for ? Do you have use cases in mind ?
> 
> The vivi driver is using it, but frankly it should be redesigned to use
> some other check.

Let's do that then :-)

> > > +
> > > +/**
> > > + * struct vb2_mem_ops - memory handling/memory allocator operations
> > > + * @alloc:	allocate video memory and, optionally, allocator private
> > > data, + *		return NULL on failure or a pointer to allocator private, +
> > > *		per-buffer data on success, NULL on failure; the returned
> > 
> > Is the "return NULL on failure" information so important that you need to
> > say it twice ? :-)
> > 
> > > + *		private structure will then be passed as buf_priv argument
> > > + *		to other ops in this structure
> > > + * @put:	inform the allocator that the buffer will no longer be used;
> > > + *		usually will result in the allocator freeing the buffer (if
> > > + *		no other users of this buffer are present); the buf_priv
> > > + *		argument is the allocator private per-buffer structure
> > > + *		previously returned from the alloc callback
> > 
> > I understand that this is the opposite of alloc. Why isn't it called free
> > ?
> 
> The problem is that not in all cases it will cause memory freeing. If you
> application mmaped the buffer and closes the file, the mapping is still
> there so the buffer cannot be freed. That's why this callback has been
> named put. If this is confusing, then maybe a 'release' will be a better
> name. 'Free' is not a good name, because it explicitly suggest that the
> memory will be freed imiedetely.

OK.

> > > + * @get_userptr: acquire userspace memory for a hardware operation;
> > > used for + *		 USERPTR memory types; vaddr is the address passed to
> > > the + *		 videobuf layer when queuing a video buffer of USERPTR 
type;
> > > + *		 should return an allocator private per-buffer structure
> > > + *		 associated with the buffer on success, NULL on failure;
> > > + *		 the returned private structure will then be passed as buf_priv
> > > + *		 argument to other ops in this structure
> > > + * @put_userptr: inform the allocator that a USERPTR buffer will no
> > > longer + *		 be used
> > > + * @vaddr:	return a kernel virtual address to a given memory buffer
> > > + *		associated with the passed private structure or NULL if no
> > > + *		such mapping exists
> > > + * @cookie:	return allocator specific cookie for a given memory buffer
> > > + *		associated with the passed private structure or NULL if not
> > > + *		available
> > > + * @num_users:	return the current number of users of a memory buffer;
> > > + *		return 1 if the videobuf layer (or actually the driver using
> > > + *		it) is the only user
> > > + * @mmap:	setup a userspace mapping for a given memory buffer under
> > > + *		the provided virtual memory region
> > 
> > Why is there no munmap() ?
> 
> Because there is no place to call such operation? munmap callback have been
> removed from file operations some time ago. You can track mapping usage by
> providing your own vm_ops with vm_open and vm_close callbacks. This is how
> it is implemented by all vb2 mem allocators.

OK, I was too tired :-)

> > > + * @buf_alloc:		called to allocate a struct vb2_buffer; driver 
usually
> > > + *			embeds it in its own custom buffer structure; returns
> > > + *			a pointer to vb2_buffer or NULL if failed; if not
> > > + *			provided kmalloc(sizeof(struct vb2_buffer, GFP_KERNEL)
> > > + *			is used
> > > + * @buf_free:		called to free the structure allocated by 
@buf_alloc;
> > > + *			if not provided kfree(vb) is used
> > 
> > I'm curious, do we have use cases for buf_alloc != kmalloc and buf_free
> > != kfree ?
> 
> Well, the problem is not which function to call, but the size that is
> passed as the argument. Drivers usually wants to embed the struct vb2
> inside its own 'buffer' structure. See vivi driver ported to vb2 for the
> reference. The driver get a pointer to vb2 and the uses containerof() to
> get his own buffer. To make it possible the buffer need to be allocated by
> the driver not the vb2. Currently I found no other way of solving this
> than such callbacks.

I would personally prefer giving the size of the driver's own buffer object to 
VB2.

> > > + * @buf_init:		called once after allocating a buffer (in MMAP case)
> > > + *			or after acquiring a new USERPTR buffer; drivers may
> > > + *			perform additional buffer-related initialization;
> > > + *			initialization failure (return != 0) will prevent
> > > + *			queue setup from completing successfully; optional
> > > + * @buf_prepare:	called every time the buffer is queued from
> > > userspace; + *			drivers may perform any initialization required
> > > before
> > > + *			each hardware operation in this callback;
> > > + *			if an error is returned, the buffer will not be queued
> > > + *			in driver; optional
> > > + * @buf_finish:		called before every dequeue of the buffer back to
> > > + *			userspace; drivers may perform any operations required
> > > + *			before userspace accesses the buffer; optional
> > > + * @buf_cleanup:	called once before the buffer is freed; drivers may
> > > + *			perform any additional cleanup; optional
> > > + * @start_streaming:	called once before entering 'streaming' state;
> > > enables + *			driver to recieve buffers over buf_queue() callback
> > > + * @stop_streaming:	called when 'streaming' state must be disabled;
> > > driver + *			should stop any dma transactions or wait until they
> > > + *			finish and give back all buffers it got from buf_queue()
> > > + *			callback
> > 
> > start_streaming is only called in response to vb2_streamon, and
> > stop_streaming in response to vb2_streamoff or vb2_release (implicit
> > streamoff). I think it would be better to require the driver to stop
> > streaming before calling vb2_streamoff and vb2_release, and have the
> > driver start streaming only when vb2_streamon returns. I don't really
> > like vb2 trying to manage the driver's streaming state. Those two
> > operations could then be removed.
> 
> Hardware streaming starts not only from STREAMON ioctl, but also from
> read/write and poll in case of file io access types. My idea was to make
> all vb2 functions to be simple callback for the respective ioctls or file
> ops, and move all the logic that need to be implemented in the driver to
> the queue callbacks. Such separation make the driver much easier to
> understand and avoids some common mistakes like forgetting to 'start
> streaming' in the poll implementation or so.

Hmmm... I'll comment on this when I'll review the patches that implement 
read()/write().

> > > +/**
> > > + * struct vb2_queue - a videobuf queue
> > > + *
> > > + * @type:	current queue type
> > > + * @memory:	current memory type used
> > 
> > current implies that the value can change over the life of the object. I
> > think that's not the case here.
> > 
> > > + * @drv_priv:	driver private data, passed on vb2_queue_init
> > 
> > What about letting drivers embed vb2_queue inside a structure of their
> > own if they need private data, and use container_of() instead of
> > drv_priv ?
> 
> I'm not sure this is a better idea. Even now you can use container_of()
> way, but I see some advantages of drv_priv approach. Imagine a driver with
> more than one queue. Usually all queues will have drv_priv pointing to the
> same place, so some common callbacks can be assigned to all of them. In
> container_of() approach callback for each queue will need to use different
> method of accessing the driver private data (different offset), so you
> will get additional almost duplicated code.

I expect many drivers to create structures to embed queues in, with a 1-1 
relationship, but there will probably be drivers that will have 2 queues in 
the same structure, so I agree with you.

-- 
Regards,

Laurent Pinchart
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux