Re: [PATCH] media: v4l2-mem2mem: Apply DST_QUEUE_OFF_BASE on MMAP buffers across ioctls

From: Chen-Yu Tsai
Date: Thu Dec 09 2021 - 11:15:47 EST


On Thu, Dec 9, 2021 at 8:06 PM Hans Verkuil <hverkuil-cisco@xxxxxxxxx> wrote:
>
> On 09/12/2021 07:29, Chen-Yu Tsai wrote:
> > DST_QUEUE_OFF_BASE is applied to offset/mem_offset on MMAP capture buffers
> > only for the VIDIOC_QUERYBUF ioctl, while the userspace fields (including
> > offset/mem_offset) are filled in for VIDIOC_{QUERY,PREPARE,Q,DQ}BUF
> > ioctls. This leads to differences in the values presented to userspace.
> > If userspace attempts to mmap the capture buffer directly using values
> > from DQBUF, it will fail.
> >
> > Move the code that applies the magic offset into a helper, and call
> > that helper from all four ioctl entry points.
> >
> > Fixes: 7f98639def42 ("V4L/DVB: add memory-to-memory device helper framework for videobuf")
> > Fixes: 908a0d7c588e ("[media] v4l: mem2mem: port to videobuf2")
> > Signed-off-by: Chen-Yu Tsai <wenst@xxxxxxxxxxxx>
> > ---
> > This was tested on RK3399 with
> >
> > gst-launch-1.0 videotestsrc num-buffers=2 ! v4l2jpegenc ! fakesink
> >
> > and verifying the values using the V4L2 debug messages:
> >
> > video2: VIDIOC_QUERYBUF: 00:00:00.000000 index=0, type=vid-cap-mplane, request_fd=0, flags=0x00004000, field=any, sequence=0, memory=mmap
> > plane 0: bytesused=0, data_offset=0x00000000, offset/userptr=0x40000000, length=2097152
> > timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
> > video2: VIDIOC_QUERYBUF: 00:00:00.000000 index=0, type=vid-out-mplane, request_fd=0, flags=0x00004000, field=any, sequence=0, memory=mmap
> > plane 0: bytesused=0, data_offset=0x00000000, offset/userptr=0x0, length=153600
> > timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
> >
> > video2: VIDIOC_QBUF: 00:00:00.000000 index=0, type=vid-cap-mplane, request_fd=0, flags=0x00004003, field=any, sequence=0, memory=mmap
> > plane 0: bytesused=2097152, data_offset=0x00000000, offset/userptr=0x40000000, length=2097152
> > timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
> > video2: VIDIOC_QBUF: 00:00:00.000000 index=0, type=vid-out-mplane, request_fd=0, flags=0x00004003, field=none, sequence=0, memory=mmap
> > plane 0: bytesused=153600, data_offset=0x00000000, offset/userptr=0x0, length=153600
> > timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
> >
> > video2: VIDIOC_DQBUF: 00:00:00.000000 index=0, type=vid-cap-mplane, request_fd=0, flags=0x00004001, field=none, sequence=0, memory=mmap
> > plane 0: bytesused=6324, data_offset=0x00000000, offset/userptr=0x40000000, length=2097152
> > timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
> > video2: VIDIOC_DQBUF: 00:00:00.000000 index=0, type=vid-out-mplane, request_fd=0, flags=0x00004001, field=none, sequence=0, memory=mmap
> > plane 0: bytesused=153600, data_offset=0x00000000, offset/userptr=0x0, length=153600
> > timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
> >
> > Gstreamer doesn't do PREPAREBUF calls, so that path was not verified.
> > However the code changes are exactly the same, so I'm quite confident
> > about them.
> >
> > ---
> > drivers/media/v4l2-core/v4l2-mem2mem.c | 46 ++++++++++++++++++++------
> > 1 file changed, 35 insertions(+), 11 deletions(-)
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
> > index e2654b422334..b47f25297c43 100644
> > --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
> > +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
> > @@ -585,19 +585,14 @@ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > }
> > EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
> >
> > -int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > - struct v4l2_buffer *buf)
> > +static void v4l2_m2m_adjust_mem_offset(struct vb2_queue *vq,
> > + struct v4l2_buffer *buf)
> > {
> > - struct vb2_queue *vq;
> > - int ret = 0;
> > - unsigned int i;
> > -
> > - vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> > - ret = vb2_querybuf(vq, buf);
> > -
> > /* Adjust MMAP memory offsets for the CAPTURE queue */
> > if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) {
> > if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
> > + unsigned int i;
> > +
> > for (i = 0; i < buf->length; ++i)
> > buf->m.planes[i].m.mem_offset
> > += DST_QUEUE_OFF_BASE;
> > @@ -605,6 +600,19 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > buf->m.offset += DST_QUEUE_OFF_BASE;
> > }
> > }
> > +}
> > +
> > +int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > + struct v4l2_buffer *buf)
> > +{
> > + struct vb2_queue *vq;
> > + int ret = 0;
> > +
> > + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> > + ret = vb2_querybuf(vq, buf);
> > +
> > + /* Adjust MMAP memory offsets for the CAPTURE queue */
> > + v4l2_m2m_adjust_mem_offset(vq, buf);
> >
> > return ret;
> > }
> > @@ -760,6 +768,10 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > }
> >
> > ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
> > +
> > + /* Adjust MMAP memory offsets for the CAPTURE queue */
> > + v4l2_m2m_adjust_mem_offset(vq, buf);
> > +
> > if (ret)
> > return ret;
> >
> > @@ -784,9 +796,15 @@ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > struct v4l2_buffer *buf)
> > {
> > struct vb2_queue *vq;
> > + int ret;
> >
> > vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> > - return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
> > + ret = vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
> > +
> > + /* Adjust MMAP memory offsets for the CAPTURE queue */
> > + v4l2_m2m_adjust_mem_offset(vq, buf);
> > +
> > + return ret;
> > }
> > EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
> >
> > @@ -795,9 +813,15 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > {
> > struct video_device *vdev = video_devdata(file);
> > struct vb2_queue *vq;
> > + int ret;
> >
> > vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> > - return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
> > + ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
> > +
> > + /* Adjust MMAP memory offsets for the CAPTURE queue */
> > + v4l2_m2m_adjust_mem_offset(vq, buf);
> > +
> > + return ret;
> > }
> > EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf);
> >
> >
>
> For all these functions you should only call v4l2_m2m_adjust_mem_offset() if !ret.
> If the vb2_* function returned an error, then the offset fields aren't filled in,
> so it makes no sense to update them. And besides, the struct isn't copied back to
> userspace anyway on error.

Got it. That makes more sense than the original code.

ChenYu