Hi all, Marek has already answered most of the comments, some of mine below.
On Thu, Nov 25, 2010 at 01:48, Marek Szyprowski <m.szyprow...@samsung.com> wrote: > Hello, > > 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.osc...@samsung.com> >> > >> > Videobuf2 is a Video for Linux 2 API-compatible driver framework for [snip] >> > +/** >> > + * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP >> > type) + * video buffer memory for all buffers/planes on the queue and >> > initializes the + * queue >> > + * >> > + * Returns the number of buffers successfully allocated. >> > + */ >> > +static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, >> > + unsigned int num_buffers, unsigned int num_planes) >> > +{ >> > + unsigned long plane_sizes[VIDEO_MAX_PLANES]; >> > + unsigned int buffer, plane; >> > + struct vb2_buffer *vb; >> > + int ret; >> > + >> > + /* Get requested plane sizes from the driver */ >> > + for (plane = 0; plane < num_planes; ++plane) { >> > + ret = call_qop(q, plane_setup, q, plane, &plane_sizes[plane]); >> > + if (ret) { >> > + dprintk(1, "Plane setup failed\n"); >> > + return ret; >> > + } >> > + } >> > + >> > + for (buffer = 0; buffer < num_buffers; ++buffer) { >> > + /* Allocate videobuf buffer structures */ >> > + vb = __vb2_buf_alloc(q); >> > + if (!vb) { >> > + dprintk(1, "Memory alloc for buffer struct failed\n"); >> > + break; >> > + } >> > + >> > + /* Length stores number of planes for multiplanar buffers */ >> > + if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) >> > + vb->v4l2_buf.length = num_planes; >> > + >> > + vb->state = VB2_BUF_STATE_DEQUEUED; >> > + vb->vb2_queue = q; >> > + vb->num_planes = num_planes; >> > + vb->v4l2_buf.index = buffer; >> > + vb->v4l2_buf.type = q->type; >> > + vb->v4l2_buf.memory = memory; >> > + >> > + /* Allocate video buffer memory for the MMAP type */ >> > + if (memory == V4L2_MEMORY_MMAP) { >> > + ret = __vb2_buf_mem_alloc(vb, plane_sizes); >> > + if (ret) { >> > + dprintk(1, "Failed allocating memory for " >> > + "buffer %d\n", buffer); >> > + __vb2_buf_free(q, vb); >> > + break; >> >> You're not cleaning up the other allocated buffers in case of failure, is >> that >> on purpose ? >> >> BTW, if allocation for the requested number of buffers fails, it would be >> nice >> to fall back to a smaller number of buffers. You could just use the number of >> buffers that have been allocated successfully and return that to userspace. >> Or >> is that what you're doing already ? In that case you should run that number >> through the queue_negotiate operation again to verify the driver can live >> with >> it. > > Currently it falls back to the number of buffers that have been successfully > allocated, but you are right, that another call to queue_negotiate is required > because the driver might not be able to handle properly too small number of > buffers. It's on purpose to fall back, as Marek explained. I agree this should be renegotiated with the driver though. > > <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. > Yes, this was solely to make the code cleaner. I purposedly made most of those functions modular, because they were difficult to grasp otherwise. I would really like them to stay that way. >> >> > + 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." > Yes, I didn't like it too much either, but as Marek pointed out, that's what the spec says. > <snip> > >> > +/** >> > + * vb2_mmap() - map video buffers into application address space >> > + * @q: videobuf2 queue >> > + * @vma: vma passed to the mmap file operation handler in the driver >> > + * >> > + * Should be called from mmap file operation handler of a driver. >> > + * This function maps one plane of one of the available video buffers to >> > + * userspace. To map whole video memory allocated on reqbufs, this >> > function + * has to be called once per each plane per each buffer >> > previously allocated. + * >> > + * When the userspace application calls mmap, it passes to it an offset >> > returned + * to it earlier by the means of vidioc_querybuf handler. That >> > offset acts as + * a "cookie", which is then used to identify the plane to >> > be mapped. + * This function finds a plane with a matching offset and a >> > mapping is performed + * by the means of a provided memory operation. >> > + * >> > + * The return values from this function are intended to be directly >> > returned + * from the mmap handler in driver. >> > + */ >> > +int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) >> > +{ >> > + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; >> > + struct vb2_plane *vb_plane; >> > + struct vb2_buffer *vb; >> > + unsigned int buffer, plane; > <snip> >> > + >> > + vb = q->bufs[buffer]; >> > + vb_plane = &vb->planes[plane]; >> > + >> > + if (vb_plane->mapped) { >> > + dprintk(1, "Plane already mapped\n"); >> >> What is the reason to disallow buffers from being mapped several times ? If >> there's a valid one, it would be worth adding it in a comment here. > > I see no reason, maybe Pawel will explain it a bit more. IMHO this check can > be removed. First thing, I think we talked about that on IRC with Hans and agreed that we don't really need to map a buffer multiple times from one process. This is also carried over from vb1 as far as I can remember. I think it was there because vb1 used to allocate memory on mmap, to prevent multiple allocations. This check could be removed, but do we really need such a feature? > >> >> > + goto end; >> >> This will return 0, is that what you want ? >> >> > + } >> > + >> > + if (!mem_ops(q, plane)->mmap) { >> > + dprintk(1, "mmap not supported\n"); >> >> Can that happen ? Can we have a queue of type MMAP with an allocator not >> supporting mmap ? That seems pretty pointless to me :-) > > Well, right. This check should be moved to the reqbufs. Yeah, this is a bit overcautious. You don't need to move it though, there is a check for that in reqbufs already (__verify_mmap_ops()). > > <snip> > >> > +/** >> > + * 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. Yes vivi uses it. Why do you think it should be redesigned? Is there a better way to check whether an application is waiting for a buffer than checking whether any application is in the queue designed especially for that? :) >> > +/** >> > + * struct vb2_ops - driver-specific callbacks >> > + * >> > + * @queue_negotiate: called from a VIDIOC_REQBUFS handler, before >> > + * memory allocation; driver should return the required >> > + * number of buffers in num_buffers and the required >> > number >> > + * of planes per buffer in num_planes >> >> To be consistent with plane_setup, shouldn't this be called queue_setup ? > > Right, this name is more adequate. > We discussed this many times already, never thought it'd resurface again. I believe "negotiate" is a much better name. This function negotiates the number of buffers and planes with the driver, it can be readjusted depending on the situation. But plane_setup is not negotiating anything, just asks for sizes and uses them as provided. Pretty sure Hans will back me up on this one :) >> >> > + * @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 really didn't like how the concept of embedding structures complicated both vb1 and is now complicating vb2. Maybe we should think about going back to driver private structures and fixed-size videobuf buffers? >> > + * @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. > I agree with Marek, I remember the problems with starting streaming in OMAP hardware, but believe the simplest case should be the easiest to implement. Unless this prevents OMAP drivers from using vb2, I thnik it should stay as is. Would this cause trouble for OMAP drivers? > <snip> > >> > +/** >> > + * 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 think we'd like to make vb2 as easy to use, as possible, to encourage adoption. Sorry, but I believe that container_of doesn't make it easier to use and understand :) >> >> > + * @bufs: videobuf buffer structures >> > + * @num_buffers: number of allocated/used buffers >> > + * @vb_lock: for ioctl handler and queue state changes >> > synchronization >> > + * @queued_list: list of buffers currently queued from userspace >> > + * @done_list: list of buffers ready to be dequeued to userspace >> > + * @done_lock: lock to protect done_list list >> > + * @done_wq: waitqueue for processes waiting for buffers ready to be >> > dequeued + * @ops: driver-specific callbacks >> > + * @alloc_ctx: memory type/allocator-specific callbacks >> > + * @streaming: current streaming state >> > + * @userptr_supported: true if queue supports USERPTR types >> > + * @mmap_supported: true if queue supports MMAP types >> > + */ >> > +struct vb2_queue { >> > + enum v4l2_buf_type type; >> > + enum v4l2_memory memory; >> > + void *drv_priv; >> > + >> > +/* private: internal use only */ >> > + struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; >> >> What about dynamically allocating this based on the number of buffers ? It >> might not be worth the hassle. > > IMHO these few bytes are not worth the amount of work required to change the > design. My first implementation actually had that array dynamically allocated as well. But the code for that was quite complicated only to gain those few bytes and I decided to make this static. This is just a few bytes as Marek says and I'm willing to pay that price for simplicity and less potential bugs, not mentioning readability and maintainability. -- Best regards, Pawel Osciak -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html