diff mbox

[RFC] vb2: Push buffer allocation and freeing into drivers

Message ID 20110624141927.1c89a033@bike.lwn.net (mailing list archive)
State RFC
Headers show

Commit Message

Jonathan Corbet June 24, 2011, 8:19 p.m. UTC
Here's a little something I decided to hack on rather than addressing all
the real work I have to do.

Videobuf2 currently manages buffer allocation for drivers, even though
drivers typically encapsulate the vb2_buffer instance in a larger
structure; that requires vb2 to know the driver's structure size and
imposes a fragile "the vb2_buffer field must be first" requirement.

This patch pushes buffer allocation and freeing down into the drivers,
which is where the real knowledge of the structure layout exists.  To that
end, buffer_init() has been renamed buffer_alloc(), and it is called at
the beginning of the process.  As it happens, no in-tree driver depends on
any kind of central initialization in its buffer_init() function, so this
move causes no problems.

The patch deletes almost as much code as it adds; in particular, error
handling gets simpler.  It's compile-tested on everything I could, and run
tested with vivi and mmp-camera.  The patch is against linuxtv/for_v3.1,
so it doesn't include the mmp-camera hunks (since videobuf2 support for
that driver isn't upstream yet.)

Signed-off-by: Jonathan Corbet <corbet@lwn.net>
---
 drivers/media/video/mem2mem_testdev.c       |   20 ++++++++++-
 drivers/media/video/mx3_camera.c            |   19 +++++-----
 drivers/media/video/s5p-fimc/fimc-capture.c |   18 ++++++++--
 drivers/media/video/s5p-fimc/fimc-core.c    |   20 ++++++++++-
 drivers/media/video/sh_mobile_ceu_camera.c  |   20 ++++++-----
 drivers/media/video/videobuf2-core.c        |   49 ++++++---------------------
 drivers/media/video/vivi.c                  |   16 +++++---
 include/media/videobuf2-core.h              |   16 ++++-----
 8 files changed, 98 insertions(+), 80 deletions(-)

Comments

Jonathan Corbet June 24, 2011, 8:27 p.m. UTC | #1
On Fri, 24 Jun 2011 14:19:27 -0600
Jonathan Corbet <corbet@lwn.net> wrote:

> Here's a little something I decided to hack on rather than addressing all
> the real work I have to do.

...and while I was looking at this code, I noticed one little curious
thing:

int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
/* ... */
	/* Finally, allocate buffers and video memory */
	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
				plane_sizes);
	if (ret < 0) {
		dprintk(1, "Memory allocation failed with error: %d\n", ret);
		return ret;
	}

If you actually look at __vb2_queue_alloc(), it claims to return the
number of buffers actually allocated, and an inspection of the code bears
up that claim.  So it can never return a negative value.  Do you maybe
want "if (ret <= 0) {" there instead?  One assumes there will be few
drivers so accommodating as to work with zero buffers.

Thanks,

jon
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Marek Szyprowski June 27, 2011, 3:18 p.m. UTC | #2
Hello,

On Friday, June 24, 2011 10:27 PM Jonathan Corbet wrote:

> On Fri, 24 Jun 2011 14:19:27 -0600
> Jonathan Corbet <corbet@lwn.net> wrote:
> 
> > Here's a little something I decided to hack on rather than addressing all
> > the real work I have to do.
> 
> ...and while I was looking at this code, I noticed one little curious
> thing:
> 
> int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> {
> /* ... */
> 	/* Finally, allocate buffers and video memory */
> 	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
> 				plane_sizes);
> 	if (ret < 0) {
> 		dprintk(1, "Memory allocation failed with error: %d\n", ret);
> 		return ret;
> 	}
> 
> If you actually look at __vb2_queue_alloc(), it claims to return the
> number of buffers actually allocated, and an inspection of the code bears
> up that claim.  So it can never return a negative value.  Do you maybe
> want "if (ret <= 0) {" there instead?  One assumes there will be few
> drivers so accommodating as to work with zero buffers.

You are right. There is no point asking driver if it accepts zero buffers. 
Thanks for pointing the bug!

Best regards
Marek Szyprowski June 27, 2011, 4:09 p.m. UTC | #3
Hello,

On Friday, June 24, 2011 10:19 PM Jonathan Corbet wrote:

> Here's a little something I decided to hack on rather than addressing all
> the real work I have to do.
> 
> Videobuf2 currently manages buffer allocation for drivers, even though
> drivers typically encapsulate the vb2_buffer instance in a larger
> structure; that requires vb2 to know the driver's structure size and
> imposes a fragile "the vb2_buffer field must be first" requirement.
> 
> This patch pushes buffer allocation and freeing down into the drivers,
> which is where the real knowledge of the structure layout exists.  To that
> end, buffer_init() has been renamed buffer_alloc(), and it is called at
> the beginning of the process.  As it happens, no in-tree driver depends on
> any kind of central initialization in its buffer_init() function, so this
> move causes no problems.
> 
> The patch deletes almost as much code as it adds; in particular, error
> handling gets simpler.  It's compile-tested on everything I could, and run
> tested with vivi and mmp-camera.  The patch is against linuxtv/for_v3.1,
> so it doesn't include the mmp-camera hunks (since videobuf2 support for
> that driver isn't upstream yet.)

Thanks for your work! I really appreciate your effort for making the kernel
code better. :) However I would like to get some more comments before making
the final decision.

The main difference between buffer_init() and buffer_alloc() is the fact
that buffer_init() is called when the buffer has all internal data filled in
(like for example index) and, what is more important, memory buffers for all
planes are already allocated. This feature is used by the s5p-mfc driver 
(video codec driver for IP found on Samsung S5PC110 and Exynos4 SoC series,
not yet merged to mainline) to gather all information for the dma engine. 
The dma engine contains some kind of array for fixed number of buffer 
addresses, which can be programmed only once before starting to process 
encoding or decoding.

I considered similar solution during videobuf2 development, but decided
that having access to all information about buffer internals (index, plane
addresses) is something that might be really useful for device drivers.

Creating additional buffer_alloc() and buffer_free() callbacks (and keeping
buffer_init and buffer_cleanup) just to make the code nicer was already 
pointed to be just an over-engineering.
 
Best regards
Jonathan Corbet June 27, 2011, 4:39 p.m. UTC | #4
On Mon, 27 Jun 2011 18:09:41 +0200
Marek Szyprowski <m.szyprowski@samsung.com> wrote:

> Thanks for your work! I really appreciate your effort for making the kernel
> code better. :) However I would like to get some more comments before making
> the final decision.

That's fine - it *was* an RFC, after all...:)

> The main difference between buffer_init() and buffer_alloc() is the fact
> that buffer_init() is called when the buffer has all internal data filled in
> (like for example index) and, what is more important, memory buffers for all
> planes are already allocated.

I had thought that might be the case, but none of the in-tree drivers
needed that information.  Obviously, I don't want to break other stuff
which is coming (that driver *is* headed for mainline, right? :), so the
idea of simply repurposing buf_init() won't quite work.  Too bad, moving
it took out a lot of error-handling code.

Could this more involved initialization code move to buf_prepare(),
perhaps with a flag in the buffer for stuff that only has to happen once?
Or maybe there could be a highly optional buf_map() (or some such) for
this kind of special-case driver?  

> I considered similar solution during videobuf2 development, but decided
> that having access to all information about buffer internals (index, plane
> addresses) is something that might be really useful for device drivers.

I guess the question is: is it sufficiently useful to enough drivers to
create a separate callback for?  I'm not convinced...but I could certainly
be wrong about that.

> Creating additional buffer_alloc() and buffer_free() callbacks (and keeping
> buffer_init and buffer_cleanup) just to make the code nicer was already 
> pointed to be just an over-engineering.

I don't think there's a need for a buf_free(), given that buf_cleanup() is
already there.  But I really dislike the "vb2_buffer must be the first
structure field" requirement; it's fragile in the long term.  The kernel
has usually gone out of its way to avoid adding that kind of hidden
constraint.

Oh, well.  I'd like to see the change merged, I think it makes things a
little better.  But I've said my piece now and don't intend to argue it
further - I'll keep using vb2 either way :)

Thanks,

jon
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pawel Osciak July 20, 2011, 4:16 a.m. UTC | #5
Hi Jon,
Thanks for your patch. I agree I'm not particularly proud of how
allocation looks like right now and of the "first structure field"
requirement. I had similar design dilemmas, but have to agree with
Marek here though. Please see my explanation below.

On Mon, Jun 27, 2011 at 09:39, Jonathan Corbet <corbet@lwn.net> wrote:
> On Mon, 27 Jun 2011 18:09:41 +0200
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>
>> Thanks for your work! I really appreciate your effort for making the kernel
>> code better. :) However I would like to get some more comments before making
>> the final decision.
>
> That's fine - it *was* an RFC, after all...:)
>
>> The main difference between buffer_init() and buffer_alloc() is the fact
>> that buffer_init() is called when the buffer has all internal data filled in
>> (like for example index) and, what is more important, memory buffers for all
>> planes are already allocated.
>
> I had thought that might be the case, but none of the in-tree drivers
> needed that information.  Obviously, I don't want to break other stuff
> which is coming (that driver *is* headed for mainline, right? :), so the
> idea of simply repurposing buf_init() won't quite work.  Too bad, moving
> it took out a lot of error-handling code.
>

One thing that I think wasn't mentioned was that you did remove
buf_init call from __qbuf_userptr(), which removes some functionality
and symmetry. In USERPTR case we still want to call buf_init(), as
it's used to perform operations that are to be done once per buffer,
after it is ready. There is no allocation in USERPTR's case though. So
it's not as much a feature for particular drivers, but to accommodate
USERPTR. For MMAP, we could do those things in buf_alloc().

> Could this more involved initialization code move to buf_prepare(),
> perhaps with a flag in the buffer for stuff that only has to happen once?
> Or maybe there could be a highly optional buf_map() (or some such) for
> this kind of special-case driver?
>

buf_prepare() is called once per frame, so checking for this flag
every frame would add some unpleasant overhead.

>> I considered similar solution during videobuf2 development, but decided
>> that having access to all information about buffer internals (index, plane
>> addresses) is something that might be really useful for device drivers.
>
> I guess the question is: is it sufficiently useful to enough drivers to
> create a separate callback for?  I'm not convinced...but I could certainly
> be wrong about that.
>

You mean buf_init(), right? Well, I aimed for this nice symmetry:
buf_init() - buf_cleanup()
buf_prepare() - buf_finish()

I agree many drivers might not need such fine-grained design, but as
we could find sensible use cases for all of them, all of them were
kept.

>> Creating additional buffer_alloc() and buffer_free() callbacks (and keeping
>> buffer_init and buffer_cleanup) just to make the code nicer was already
>> pointed to be just an over-engineering.
>
> I don't think there's a need for a buf_free(), given that buf_cleanup() is
> already there.  But I really dislike the "vb2_buffer must be the first
> structure field" requirement; it's fragile in the long term.  The kernel
> has usually gone out of its way to avoid adding that kind of hidden
> constraint.
>

As with buf_init() and allocation, you don't always free the memory
when you buf_cleanup(), which happens in USERPTR case.

> Oh, well.  I'd like to see the change merged, I think it makes things a
> little better.  But I've said my piece now and don't intend to argue it
> further - I'll keep using vb2 either way :)
>
> Thanks,
>
> jon
>
diff mbox

Patch

diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index b03d74e..05d8f62 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -789,6 +789,22 @@  static int m2mtest_buf_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
+static struct vb2_buffer *m2mtest_buf_alloc(struct vb2_queue *q)
+{
+	struct v4l2_m2m_buffer *buf;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (buf == NULL)
+		return NULL;
+	return &buf->vb;
+}
+
+static void m2mtest_buf_cleanup(struct vb2_buffer *vb)
+{
+	kfree(container_of(vb, struct v4l2_m2m_buffer, vb));
+}
+
+
 static void m2mtest_buf_queue(struct vb2_buffer *vb)
 {
 	struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
@@ -797,6 +813,8 @@  static void m2mtest_buf_queue(struct vb2_buffer *vb)
 
 static struct vb2_ops m2mtest_qops = {
 	.queue_setup	 = m2mtest_queue_setup,
+	.buf_alloc	 = m2mtest_buf_alloc,
+	.buf_cleanup	 = m2mtest_buf_cleanup,
 	.buf_prepare	 = m2mtest_buf_prepare,
 	.buf_queue	 = m2mtest_buf_queue,
 };
@@ -810,7 +828,6 @@  static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
 	src_vq->io_modes = VB2_MMAP;
 	src_vq->drv_priv = ctx;
-	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	src_vq->ops = &m2mtest_qops;
 	src_vq->mem_ops = &vb2_vmalloc_memops;
 
@@ -822,7 +839,6 @@  static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	dst_vq->io_modes = VB2_MMAP;
 	dst_vq->drv_priv = ctx;
-	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	dst_vq->ops = &m2mtest_qops;
 	dst_vq->mem_ops = &vb2_vmalloc_memops;
 
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index c7680eb..fc981d4 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -68,7 +68,6 @@  enum csi_buffer_state {
 };
 
 struct mx3_camera_buffer {
-	/* common v4l buffer stuff -- must be first */
 	struct vb2_buffer			vb;
 	enum csi_buffer_state			state;
 	struct list_head			queue;
@@ -374,10 +373,6 @@  static void mx3_videobuf_release(struct vb2_buffer *vb)
 	if (mx3_cam->active == buf)
 		mx3_cam->active = NULL;
 
-	/* Doesn't hurt also if the list is empty */
-	list_del_init(&buf->queue);
-	buf->state = CSI_BUF_NEEDS_INIT;
-
 	if (txd) {
 		buf->txd = NULL;
 		if (mx3_cam->idmac_channel[0])
@@ -385,11 +380,16 @@  static void mx3_videobuf_release(struct vb2_buffer *vb)
 	}
 
 	spin_unlock_irqrestore(&mx3_cam->lock, flags);
+	kfree(buf);
 }
 
-static int mx3_videobuf_init(struct vb2_buffer *vb)
+static struct vb2_buffer *mx3_videobuf_alloc(struct vb2_queue *q)
 {
-	struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+	struct mx3_camera_buffer *buf;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (buf == NULL)
+		return NULL;
 	/* This is for locking debugging only */
 	INIT_LIST_HEAD(&buf->queue);
 	sg_init_table(&buf->sg, 1);
@@ -397,7 +397,7 @@  static int mx3_videobuf_init(struct vb2_buffer *vb)
 	buf->state = CSI_BUF_NEEDS_INIT;
 	buf->txd = NULL;
 
-	return 0;
+	return &buf->vb;
 }
 
 static int mx3_stop_streaming(struct vb2_queue *q)
@@ -434,7 +434,7 @@  static struct vb2_ops mx3_videobuf_ops = {
 	.buf_prepare	= mx3_videobuf_prepare,
 	.buf_queue	= mx3_videobuf_queue,
 	.buf_cleanup	= mx3_videobuf_release,
-	.buf_init	= mx3_videobuf_init,
+	.buf_alloc	= mx3_videobuf_alloc,
 	.wait_prepare	= soc_camera_unlock,
 	.wait_finish	= soc_camera_lock,
 	.stop_streaming	= mx3_stop_streaming,
@@ -448,7 +448,6 @@  static int mx3_camera_init_videobuf(struct vb2_queue *q,
 	q->drv_priv = icd;
 	q->ops = &mx3_videobuf_ops;
 	q->mem_ops = &vb2_dma_contig_memops;
-	q->buf_struct_size = sizeof(struct mx3_camera_buffer);
 
 	return vb2_queue_init(q);
 }
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index d142b40..0f2b2b7 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -295,10 +295,20 @@  static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
 	return 0;
 }
 
-static int buffer_init(struct vb2_buffer *vb)
+static struct vb2_buffer *buffer_alloc(struct vb2_queue *q)
 {
+	struct fimc_vid_buffer *buf;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (buf == NULL)
+		return NULL;
 	/* TODO: */
-	return 0;
+	return &buf->vb;
+}
+
+static void buffer_cleanup(struct vb2_buffer *buf)
+{
+	kfree(container_of(buf, struct fimc_vid_buffer, vb));
 }
 
 static int buffer_prepare(struct vb2_buffer *vb)
@@ -380,7 +390,8 @@  static struct vb2_ops fimc_capture_qops = {
 	.queue_setup		= queue_setup,
 	.buf_prepare		= buffer_prepare,
 	.buf_queue		= buffer_queue,
-	.buf_init		= buffer_init,
+	.buf_alloc		= buffer_alloc,
+	.buf_cleanup		= buffer_cleanup,
 	.wait_prepare		= fimc_unlock,
 	.wait_finish		= fimc_lock,
 	.start_streaming	= start_streaming,
@@ -882,7 +893,6 @@  int fimc_register_capture_device(struct fimc_dev *fimc)
 	q->drv_priv = fimc->vid_cap.ctx;
 	q->ops = &fimc_capture_qops;
 	q->mem_ops = &vb2_dma_contig_memops;
-	q->buf_struct_size = sizeof(struct fimc_vid_buffer);
 
 	vb2_queue_init(q);
 
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index dc91a85..6810115 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -730,6 +730,22 @@  static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
 	return 0;
 }
 
+static struct vb2_buffer *fimc_buf_alloc(struct vb2_queue *q)
+{
+	struct v4l2_m2m_buffer *buf;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (buf == NULL)
+		return NULL;
+	return &buf->vb;
+}
+
+static void fimc_buf_cleanup(struct vb2_buffer *vb)
+{
+	kfree(container_of(vb, struct v4l2_m2m_buffer, vb));
+}
+
+
 static int fimc_buf_prepare(struct vb2_buffer *vb)
 {
 	struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
@@ -770,6 +786,8 @@  static void fimc_unlock(struct vb2_queue *vq)
 
 static struct vb2_ops fimc_qops = {
 	.queue_setup	 = fimc_queue_setup,
+	.buf_alloc	 = fimc_buf_alloc,
+	.buf_cleanup	 = fimc_buf_cleanup,
 	.buf_prepare	 = fimc_buf_prepare,
 	.buf_queue	 = fimc_buf_queue,
 	.wait_prepare	 = fimc_unlock,
@@ -1390,7 +1408,6 @@  static int queue_init(void *priv, struct vb2_queue *src_vq,
 	src_vq->drv_priv = ctx;
 	src_vq->ops = &fimc_qops;
 	src_vq->mem_ops = &vb2_dma_contig_memops;
-	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 
 	ret = vb2_queue_init(src_vq);
 	if (ret)
@@ -1402,7 +1419,6 @@  static int queue_init(void *priv, struct vb2_queue *src_vq,
 	dst_vq->drv_priv = ctx;
 	dst_vq->ops = &fimc_qops;
 	dst_vq->mem_ops = &vb2_dma_contig_memops;
-	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 
 	return vb2_queue_init(dst_vq);
 }
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 3ae5c9c..1d15dd1 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -88,7 +88,7 @@ 
 
 /* per video frame buffer */
 struct sh_mobile_ceu_buffer {
-	struct vb2_buffer vb; /* v4l buffer must be first */
+	struct vb2_buffer vb;
 	struct list_head queue;
 	enum v4l2_mbus_pixelcode code;
 };
@@ -421,17 +421,20 @@  static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
 		pcdev->active = NULL;
 	}
 
-	/* Doesn't hurt also if the list is empty */
-	list_del_init(&buf->queue);
-
 	spin_unlock_irq(&pcdev->lock);
+	kfree(buf);
 }
 
-static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
+static struct vb2_buffer *sh_mobile_ceu_videobuf_alloc(struct vb2_queue *q)
 {
+	struct sh_mobile_ceu_buffer *buf;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (buf == NULL)
+		return NULL;
 	/* This is for locking debugging only */
-	INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
-	return 0;
+	INIT_LIST_HEAD(&buf->queue);
+	return &buf->vb;
 }
 
 static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
@@ -458,7 +461,7 @@  static struct vb2_ops sh_mobile_ceu_videobuf_ops = {
 	.buf_prepare	= sh_mobile_ceu_videobuf_prepare,
 	.buf_queue	= sh_mobile_ceu_videobuf_queue,
 	.buf_cleanup	= sh_mobile_ceu_videobuf_release,
-	.buf_init	= sh_mobile_ceu_videobuf_init,
+	.buf_alloc	= sh_mobile_ceu_videobuf_alloc,
 	.wait_prepare	= soc_camera_unlock,
 	.wait_finish	= soc_camera_lock,
 	.stop_streaming	= sh_mobile_ceu_stop_streaming,
@@ -1840,7 +1843,6 @@  static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
 	q->drv_priv = icd;
 	q->ops = &sh_mobile_ceu_videobuf_ops;
 	q->mem_ops = &vb2_dma_contig_memops;
-	q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
 
 	return vb2_queue_init(q);
 }
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 6ba1461..7837b97 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -149,8 +149,12 @@  static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 	int ret;
 
 	for (buffer = 0; buffer < num_buffers; ++buffer) {
-		/* Allocate videobuf buffer structures */
-		vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
+		/*
+		 * Allocate videobuf buffer structures
+		 * We *could* default to allocating a bare vb2_buffer
+		 * if the driver hasn't provided its own function.
+		 */
+		vb = call_qop(q, buf_alloc, q);
 		if (!vb) {
 			dprintk(1, "Memory alloc for buffer struct failed\n");
 			break;
@@ -176,19 +180,6 @@  static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 				kfree(vb);
 				break;
 			}
-			/*
-			 * Call the driver-provided buffer initialization
-			 * callback, if given. An error in initialization
-			 * results in queue setup failure.
-			 */
-			ret = call_qop(q, buf_init, vb);
-			if (ret) {
-				dprintk(1, "Buffer %d %p initialization"
-					" failed\n", buffer, vb);
-				__vb2_buf_mem_free(vb);
-				kfree(vb);
-				break;
-			}
 		}
 
 		q->bufs[buffer] = vb;
@@ -234,21 +225,14 @@  static void __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);
 
 	/* Free videobuf buffers */
 	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
-		kfree(q->bufs[buffer]);
+		if (q->bufs[buffer] == NULL)
+			continue;
+		q->ops->buf_cleanup(q->bufs[buffer]);
 		q->bufs[buffer] = NULL;
 	}
 
@@ -776,16 +760,6 @@  static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
 	}
 
 	/*
-	 * Call driver-specific initialization on the newly acquired buffer,
-	 * if provided.
-	 */
-	ret = call_qop(q, buf_init, vb);
-	if (ret) {
-		dprintk(1, "qbuf: buffer initialization failed\n");
-		goto err;
-	}
-
-	/*
 	 * Now that everything is in order, copy relevant information
 	 * provided by userspace.
 	 */
@@ -1442,15 +1416,14 @@  int vb2_queue_init(struct vb2_queue *q)
 
 	BUG_ON(!q->ops->queue_setup);
 	BUG_ON(!q->ops->buf_queue);
+	BUG_ON(!q->ops->buf_alloc);
+	BUG_ON(!q->ops->buf_cleanup);
 
 	INIT_LIST_HEAD(&q->queued_list);
 	INIT_LIST_HEAD(&q->done_list);
 	spin_lock_init(&q->done_lock);
 	init_waitqueue_head(&q->done_wq);
 
-	if (q->buf_struct_size == 0)
-		q->buf_struct_size = sizeof(struct vb2_buffer);
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(vb2_queue_init);
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 2238a61..9a1611f 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -138,7 +138,6 @@  static struct vivi_fmt *get_format(struct v4l2_format *f)
 
 /* buffer for one video frame */
 struct vivi_buffer {
-	/* common v4l buffer stuff -- must be first */
 	struct vb2_buffer	vb;
 	struct list_head	list;
 	struct vivi_fmt        *fmt;
@@ -673,9 +672,14 @@  static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
 	return 0;
 }
 
-static int buffer_init(struct vb2_buffer *vb)
+static struct vb2_buffer *buffer_alloc(struct vb2_queue *q)
 {
-	struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct vivi_buffer *buf;
+	struct vivi_dev *dev = vb2_get_drv_priv(q);
+
+	buf = kzalloc(sizeof(struct vivi_buffer), GFP_KERNEL);
+	if (buf == NULL)
+		return NULL;
 
 	BUG_ON(NULL == dev->fmt);
 
@@ -691,7 +695,7 @@  static int buffer_init(struct vb2_buffer *vb)
 	 * s_fmt though.
 	 */
 
-	return 0;
+	return &buf->vb;
 }
 
 static int buffer_prepare(struct vb2_buffer *vb)
@@ -743,6 +747,7 @@  static void buffer_cleanup(struct vb2_buffer *vb)
 	struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	dprintk(dev, 1, "%s\n", __func__);
 
+	kfree(container_of(vb, struct vivi_buffer, vb));
 }
 
 static void buffer_queue(struct vb2_buffer *vb)
@@ -790,7 +795,7 @@  static void vivi_unlock(struct vb2_queue *vq)
 
 static struct vb2_ops vivi_video_qops = {
 	.queue_setup		= queue_setup,
-	.buf_init		= buffer_init,
+	.buf_alloc		= buffer_alloc,
 	.buf_prepare		= buffer_prepare,
 	.buf_finish		= buffer_finish,
 	.buf_cleanup		= buffer_cleanup,
@@ -1234,7 +1239,6 @@  static int __init vivi_create_instance(int inst)
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
 	q->drv_priv = dev;
-	q->buf_struct_size = sizeof(struct vivi_buffer);
 	q->ops = &vivi_video_qops;
 	q->mem_ops = &vb2_vmalloc_memops;
 
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index f87472a..957240b 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -184,11 +184,11 @@  struct vb2_buffer {
  * @wait_finish:	reacquire all locks released in the previous callback;
  *			required to continue operation after sleeping while
  *			waiting for a new buffer to arrive
- * @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_alloc:		Called once to allocate each new buffer; should
+ *			perform any driver-specific initialization and
+ *			return a pointer to the (possibly encapsulated)
+ *			vb2_buffer structure.  Returning NULL here will
+ *			abort queue setup.
  * @buf_prepare:	called every time the buffer is queued from userspace;
  *			drivers may perform any initialization required before
  *			each hardware operation in this callback;
@@ -197,8 +197,7 @@  struct vb2_buffer {
  * @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
+ * @buf_cleanup:	called once to clean up and free each buffer.
  * @start_streaming:	called once before entering 'streaming' state; enables
  *			driver to receive buffers over buf_queue() callback
  * @stop_streaming:	called when 'streaming' state must be disabled; driver
@@ -217,7 +216,7 @@  struct vb2_ops {
 	void (*wait_prepare)(struct vb2_queue *q);
 	void (*wait_finish)(struct vb2_queue *q);
 
-	int (*buf_init)(struct vb2_buffer *vb);
+	struct vb2_buffer *(*buf_alloc)(struct vb2_queue *q);
 	int (*buf_prepare)(struct vb2_buffer *vb);
 	int (*buf_finish)(struct vb2_buffer *vb);
 	void (*buf_cleanup)(struct vb2_buffer *vb);
@@ -261,7 +260,6 @@  struct vb2_queue {
 	const struct vb2_ops		*ops;
 	const struct vb2_mem_ops	*mem_ops;
 	void				*drv_priv;
-	unsigned int			buf_struct_size;
 
 /* private: internal use only */
 	enum v4l2_memory		memory;