From: Alexandre Courbot <[email protected]> This patch adds support for the USERPTR memory type to the virtio-media driver.
It adds the allow_userptr module parameter, implements the userptr mapping logic in the scatterlist builder, and enables USERPTR in reqbufs if allowed. Signed-off-by: Alexandre Courbot <[email protected]> Assisted-by: Antigravity:gemini-3.5-flash Co-developed-by: Brian Daniels <[email protected]> Signed-off-by: Brian Daniels <[email protected]> --- drivers/media/virtio/scatterlist_builder.c | 64 ++++++++++++++++++++++ drivers/media/virtio/scatterlist_builder.h | 3 + drivers/media/virtio/virtio_media_driver.c | 42 +++++++++----- drivers/media/virtio/virtio_media_ioctls.c | 11 +++- 4 files changed, 104 insertions(+), 16 deletions(-) diff --git a/drivers/media/virtio/scatterlist_builder.c b/drivers/media/virtio/scatterlist_builder.c index 97925b277..85c6a36b4 100644 --- a/drivers/media/virtio/scatterlist_builder.c +++ b/drivers/media/virtio/scatterlist_builder.c @@ -349,14 +349,30 @@ static int scatterlist_builder_add_userptr(struct scatterlist_builder *builder, int scatterlist_builder_add_buffer(struct scatterlist_builder *builder, struct v4l2_buffer *b) { + int i; int ret; + /* Fixup: plane length must be zero if userptr is NULL */ + if (!V4L2_TYPE_IS_MULTIPLANAR(b->type) && + b->memory == V4L2_MEMORY_USERPTR && b->m.userptr == 0) + b->length = 0; + /* v4l2_buffer */ ret = scatterlist_builder_add_data(builder, b, sizeof(*b)); if (ret) return ret; if (V4L2_TYPE_IS_MULTIPLANAR(b->type) && b->length > 0) { + /* Fixup: plane length must be zero if userptr is NULL */ + if (b->memory == V4L2_MEMORY_USERPTR) { + for (i = 0; i < b->length; i++) { + struct v4l2_plane *plane = &b->m.planes[i]; + + if (plane->m.userptr == 0) + plane->length = 0; + } + } + /* Array of v4l2_planes */ ret = scatterlist_builder_add_data(builder, b->m.planes, sizeof(struct v4l2_plane) * @@ -368,6 +384,54 @@ int scatterlist_builder_add_buffer(struct scatterlist_builder *builder, return 0; } +/** + * scatterlist_builder_add_buffer_userptr() - Add the payload of a ``USERPTR`` + * &struct v4l2_buffer to the + * descriptor chain. + * @builder: builder to use. + * @b: &struct v4l2_buffer whose ``USERPTR`` payload we want to add. + * + * Add an array of &struct virtio_media_sg_entry pointing to a ``USERPTR`` + * buffer's contents. Does nothing if the buffer is not of type ``USERPTR``. + * This is split out of scatterlist_builder_add_buffer() because we only want + * to add these to the device-readable part of the descriptor chain. + */ +int scatterlist_builder_add_buffer_userptr(struct scatterlist_builder *builder, + struct v4l2_buffer *b) +{ + int i; + int ret; + + if (b->memory != V4L2_MEMORY_USERPTR) + return 0; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + for (i = 0; i < b->length; i++) { + struct v4l2_plane *plane = &b->m.planes[i]; + + if (b->memory == V4L2_MEMORY_USERPTR && + plane->length > 0) { + unsigned long uptr = plane->m.userptr; + unsigned long len = plane->length; + + ret = + scatterlist_builder_add_userptr(builder, + uptr, + len); + if (ret) + return ret; + } + } + } else if (b->length > 0) { + ret = scatterlist_builder_add_userptr(builder, b->m.userptr, + b->length); + if (ret) + return ret; + } + + return 0; +} + /** * scatterlist_builder_retrieve_buffer() - Retrieve a &struct v4l2_buffer * written by the device on the shadow diff --git a/drivers/media/virtio/scatterlist_builder.h b/drivers/media/virtio/scatterlist_builder.h index 47bfd7ae0..53d964a48 100644 --- a/drivers/media/virtio/scatterlist_builder.h +++ b/drivers/media/virtio/scatterlist_builder.h @@ -90,6 +90,9 @@ int scatterlist_builder_add_ioctl_resp(struct scatterlist_builder *builder, int scatterlist_builder_add_buffer(struct scatterlist_builder *builder, struct v4l2_buffer *buffer); +int scatterlist_builder_add_buffer_userptr(struct scatterlist_builder *builder, + struct v4l2_buffer *b); + int scatterlist_builder_retrieve_buffer(struct scatterlist_builder *builder, size_t sg_index, struct v4l2_buffer *buffer, diff --git a/drivers/media/virtio/virtio_media_driver.c b/drivers/media/virtio/virtio_media_driver.c index c431c3eb2..b6f79593d 100644 --- a/drivers/media/virtio/virtio_media_driver.c +++ b/drivers/media/virtio/virtio_media_driver.c @@ -7,26 +7,29 @@ */ #include <linux/bits.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/dev_printk.h> +#include <linux/mm.h> #include <linux/mutex.h> +#include <linux/scatterlist.h> #include <linux/types.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <linux/workqueue.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/virtio.h> #include <linux/virtio_config.h> #include <linux/virtio_ids.h> -#include <linux/slab.h> -#include <linux/scatterlist.h> -#include <linux/vmalloc.h> -#include <linux/workqueue.h> -#include <linux/dma-mapping.h> -#include <linux/poll.h> -#include <linux/mm.h> +#include <media/frame_vector.h> #include <media/v4l2-dev.h> -#include <media/v4l2-device.h> -#include <media/v4l2-fh.h> #include <media/v4l2-event.h> +#include <media/videobuf2-memops.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> #include "uapi/linux/virtio_media.h" #include "session.h" @@ -40,6 +43,15 @@ /* Bit mask for the VIRTIO_MEDIA_MMAP_FLAG_RW flag */ #define VIRTIO_MEDIA_MMAP_FLAG_RW_MASK BIT(VIRTIO_MEDIA_MMAP_FLAG_RW) +/* + * Whether USERPTR buffers are allowed. + * + * This is disabled by default as USERPTR buffers are dangerous, but the option + * is left to enable them if desired. + */ +bool virtio_media_allow_userptr; +module_param_named(allow_userptr, virtio_media_allow_userptr, bool, 0660); + /** * virtio_media_session_alloc() - Allocate a new session. * @vv: virtio-media device the session belongs to. @@ -849,15 +861,11 @@ static int virtio_media_probe(struct virtio_device *virtio_dev) VIRTIO_MEDIA_SHM_MMAP); vd = &vv->video_dev; + vd->v4l2_dev = &vv->v4l2_dev; vd->vfl_type = VFL_TYPE_VIDEO; vd->ioctl_ops = &virtio_media_ioctl_ops; vd->fops = &virtio_media_fops; - vd->release = video_device_release_empty; - strscpy(vd->name, "virtio-media", sizeof(vd->name)); - - video_set_drvdata(vd, vv); - vd->device_caps = virtio_cread32(virtio_dev, 0); if (vd->device_caps & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE)) vd->vfl_dir = VFL_DIR_M2M; @@ -866,6 +874,10 @@ static int virtio_media_probe(struct virtio_device *virtio_dev) vd->vfl_dir = VFL_DIR_TX; else vd->vfl_dir = VFL_DIR_RX; + vd->release = video_device_release_empty; + strscpy(vd->name, "virtio-media", sizeof(vd->name)); + + video_set_drvdata(vd, vv); ret = video_register_device(vd, virtio_cread32(virtio_dev, 4), 0); if (ret) @@ -890,6 +902,7 @@ static int virtio_media_probe(struct virtio_device *virtio_dev) virtio_dev->config->del_vqs(virtio_dev); err_find_vqs: v4l2_device_unregister(&vv->v4l2_dev); + return ret; } @@ -900,6 +913,7 @@ static void virtio_media_remove(struct virtio_device *virtio_dev) cancel_work_sync(&vv->eventq_work); virtio_reset_device(virtio_dev); + v4l2_device_unregister(&vv->v4l2_dev); virtio_dev->config->del_vqs(virtio_dev); video_unregister_device(&vv->video_dev); diff --git a/drivers/media/virtio/virtio_media_ioctls.c b/drivers/media/virtio/virtio_media_ioctls.c index f0b82b5ec..88465f239 100644 --- a/drivers/media/virtio/virtio_media_ioctls.c +++ b/drivers/media/virtio/virtio_media_ioctls.c @@ -273,6 +273,12 @@ static int virtio_media_send_buffer_ioctl(struct v4l2_fh *fh, u32 ioctl, return ret; end_buf_sg = builder.cur_sg; + + /* Payload of SHARED_PAGES buffers, if relevant */ + ret = scatterlist_builder_add_buffer_userptr(&builder, b); + if (ret < 0) + return ret; + num_cmd_sgs = builder.cur_sg; /* Response descriptor */ @@ -719,7 +725,7 @@ static int virtio_media_reqbufs(struct file *file, void *fh, if (b->type > VIRTIO_MEDIA_LAST_QUEUE) return -EINVAL; - if (b->memory == V4L2_MEMORY_USERPTR) + if (b->memory == V4L2_MEMORY_USERPTR && !virtio_media_allow_userptr) return -EINVAL; ret = virtio_media_send_wr_ioctl(vfh, VIDIOC_REQBUFS, b, sizeof(*b), @@ -752,7 +758,8 @@ static int virtio_media_reqbufs(struct file *file, void *fh, if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) session->uses_mplane = true; - b->capabilities &= ~V4L2_BUF_CAP_SUPPORTS_USERPTR; + if (!virtio_media_allow_userptr) + b->capabilities &= ~V4L2_BUF_CAP_SUPPORTS_USERPTR; /* We do not support DMABUF yet. */ b->capabilities &= ~V4L2_BUF_CAP_SUPPORTS_DMABUF; -- 2.55.0.229.g6434b31f56-goog

