qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Albert Esteve <aesteve@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Thomas Huth" <thuth@redhat.com>,
	fmartine@redhat.com, "Gerd Hoffmann" <kraxel@redhat.com>,
	eballetb@redhat.com, "Albert Esteve" <aesteve@redhat.com>,
	alex.bennee@linaro.org, "Laurent Vivier" <lvivier@redhat.com>,
	"Michael S. Tsirkin" <mst@redhat.com>,
	sgarzare@redhat.com,
	"Marc-André Lureau" <marcandre.lureau@redhat.com>,
	peter.griffin@linaro.org, "Paolo Bonzini" <pbonzini@redhat.com>
Subject: [PATCH 12/12] Add support for v4l2_memory_dmabuf
Date: Wed, 22 Mar 2023 15:21:32 +0100	[thread overview]
Message-ID: <20230322142132.22909-13-aesteve@redhat.com> (raw)
In-Reply-To: <20230322142132.22909-1-aesteve@redhat.com>

Support VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT feature,
by importing DMA buffers using the v4l2 API.

In this patch, we do not import the buffer from
any other virtio device, but just create a DMA
buffer through udmabuf, and import it.

However, in preparation for the case where we
actually share buffers between different devices, we
store the UUID and keep buffers associated to them
in a hash table.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
 tools/vhost-user-video/v4l2_backend.c     | 118 +++++++++++++++++++---
 tools/vhost-user-video/v4l2_backend.h     |   9 +-
 tools/vhost-user-video/vhost-user-video.c |  34 ++++---
 3 files changed, 131 insertions(+), 30 deletions(-)

diff --git a/tools/vhost-user-video/v4l2_backend.c b/tools/vhost-user-video/v4l2_backend.c
index 2f5825a733..e7c03a1c70 100644
--- a/tools/vhost-user-video/v4l2_backend.c
+++ b/tools/vhost-user-video/v4l2_backend.c
@@ -888,11 +888,55 @@ int video_streamoff(struct stream *s, enum v4l2_buf_type type)
     return ret;
 }
 
+int v4l2_dmabuf_lookup_mplane(struct vuvbm_device *dev,
+                              struct resource *res,
+                              unsigned int iov_cnt)
+{
+    struct VuVideoDMABuf *buf = vuvbm_lookup(dev, res->uuid);
+    if (!buf) {
+        g_debug("Buffer not found. Creating.");
+        res->buf = g_malloc0(sizeof(struct VuVideoDMABuf) * iov_cnt);
+        for (int i = 0; i < iov_cnt; i++) {
+            if (!vuvbm_buffer_create(dev, &res->buf[i], res->iov[i].iov_len)) {
+                return -1;
+            }
+        }
+        g_debug("Inserting buffer into the table.");
+        g_hash_table_insert(dev->resource_uuids, res->buf, &res->uuid);
+    } else {
+        res->buf = buf;
+        g_debug("Buffer found.");
+    }
+
+    return 0;
+}
+
+int v4l2_dmabuf_lookup(struct vuvbm_device *dev,
+                       struct resource *res,
+                       unsigned int iov_len)
+{
+    struct VuVideoDMABuf *buf = vuvbm_lookup(dev, res->uuid);
+    if (!buf) {
+        g_debug("Buffer not found. Creating.");
+        res->buf = g_new0(struct VuVideoDMABuf, 1);
+        if (!vuvbm_buffer_create(dev, res->buf, iov_len)) {
+            return -1;
+        }
+        g_debug("Inserting buffer into the table.");
+        g_hash_table_insert(dev->resource_uuids, res->buf, &res->uuid);
+    } else {
+        res->buf = buf;
+        g_debug("Buffer found.");
+    }
+
+    return 0;
+}
+
 int v4l2_queue_buffer(enum v4l2_buf_type type,
                       enum v4l2_memory memory,
                       struct virtio_video_resource_queue *qcmd,
                       struct resource *res, struct stream *s,
-                      struct v4l2_device *dev)
+                      struct v4l2_device *dev, struct vuvbm_device *bm_dev)
 {
     struct v4l2_buffer vbuf;
     int ret = 0;
@@ -911,22 +955,64 @@ int v4l2_queue_buffer(enum v4l2_buf_type type,
 
     convert_to_timeval(le64toh(qcmd->timestamp), &vbuf.timestamp);
 
-    if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
-        /* for mplane length field is number of elements in planes array */
-        vbuf.length = res->vio_resource.num_planes;
-        vbuf.m.planes = g_malloc0(sizeof(struct v4l2_plane)
-                                  * res->vio_resource.num_planes);
+    if (memory == V4L2_MEMORY_USERPTR) {
+        if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+            /* for mplane length field is number of elements in planes array */
+            vbuf.length = res->vio_resource.num_planes;
+            vbuf.m.planes = g_malloc0(sizeof(struct v4l2_plane)
+                                    * res->vio_resource.num_planes);
 
-        for (int i = 0; i < vbuf.length; i++) {
-            vbuf.m.planes[i].m.userptr = (unsigned long)res->iov[i].iov_base;
-            vbuf.m.planes[i].length = (unsigned long)res->iov[i].iov_len;
+            for (int i = 0; i < vbuf.length; i++) {
+                vbuf.m.planes[i].m.userptr =
+                    (unsigned long)res->iov[i].iov_base;
+                vbuf.m.planes[i].length = (unsigned long)res->iov[i].iov_len;
+            }
+        } else {
+            assert(res->iov != NULL);
+            vbuf.m.userptr = (unsigned long)res->iov[0].iov_base;
+            vbuf.length = res->iov[0].iov_len;
+            g_debug("%s: iov_base = 0x%p", __func__, res->iov[0].iov_base);
+            g_debug("%s: iov_len = 0x%lx", __func__, res->iov[0].iov_len);
+        }
+    } else {
+        assert(memory == V4L2_MEMORY_DMABUF);
+        if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+            vbuf.length = res->vio_resource.num_planes;
+            vbuf.m.planes = g_malloc0(sizeof(struct v4l2_plane) * vbuf.length);
+            if (!res->buf) {
+                ret = v4l2_dmabuf_lookup_mplane(bm_dev, res, vbuf.length);
+                if (ret < 0) {
+                    g_warning("Buffer create failed.");
+                    return ret;
+                }
+            }
+
+            for (int i = 0; i < vbuf.length; i++) {
+                vbuf.m.planes[i].m.fd = res->buf->dev->get_fd(&res->buf[i]);
+                vbuf.m.planes[i].length = (unsigned long)res->iov[i].iov_len;
+                /* Copy virtio shared memory contents to DMA buffer */
+                memcpy(res->buf[i].start,
+                       res->iov[i].iov_base, res->iov[i].iov_len);
+            }
+        } else {
+            if (!res->buf) {
+                ret = ioctl(fd, VIDIOC_QUERYBUF, &vbuf);
+                if (ret == -EINVAL) {
+                    g_printerr("VIDIOC_QUERYBUF failed: %s (%d)\n",
+                               g_strerror(errno), errno);
+                    return ret;
+                }
+
+                ret = v4l2_dmabuf_lookup(bm_dev, res, vbuf.length);
+                if (ret < 0) {
+                    g_warning("Buffer create failed.");
+                    return ret;
+                }
+            }
+            vbuf.m.fd = res->buf->dev->get_fd(res->buf);
+            /* Copy virtio shared memory contents to DMA buffer */
+            memcpy(res->buf->start, res->iov[0].iov_base, res->iov[0].iov_len);
         }
-    } else if (res->iov != NULL) {
-        /* m is a union of userptr, *planes and fd */
-        vbuf.m.userptr = (unsigned long)res->iov[0].iov_base;
-        vbuf.length = res->iov[0].iov_len;
-        g_debug("%s: iov_base = 0x%p", __func__, res->iov[0].iov_base);
-        g_debug("%s: iov_len = 0x%lx", __func__, res->iov[0].iov_len);
     }
 
     if (V4L2_TYPE_IS_OUTPUT(type)) {
@@ -979,7 +1065,7 @@ int v4l2_dequeue_buffer(int fd, enum v4l2_buf_type type,
     memset(&vbuf, 0, sizeof(vbuf));
 
     vbuf.type = type;
-    vbuf.memory =  memory;
+    vbuf.memory = memory;
 
     vbuf.field = V4L2_FIELD_NONE;
     vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
diff --git a/tools/vhost-user-video/v4l2_backend.h b/tools/vhost-user-video/v4l2_backend.h
index ef9a5f0337..05409e03d4 100644
--- a/tools/vhost-user-video/v4l2_backend.h
+++ b/tools/vhost-user-video/v4l2_backend.h
@@ -69,11 +69,18 @@ int v4l2_video_set_format(int fd, enum v4l2_buf_type type,
 int v4l2_set_pixel_format(int fd, enum v4l2_buf_type buf_type,
                           uint32_t pixelformat);
 
+int v4l2_dmabuf_lookup_mplane(struct vuvbm_device *dev,
+                              struct resource *res,
+                              unsigned int iov_cnt);
+int v4l2_dmabuf_lookup(struct vuvbm_device *dev,
+                       struct resource *res,
+                       unsigned int iov_len);
 int v4l2_queue_buffer(enum v4l2_buf_type type,
                       enum v4l2_memory memory,
                       struct virtio_video_resource_queue *qcmd,
                       struct resource *res, struct stream *s,
-                      struct v4l2_device *dev);
+                      struct v4l2_device *dev,
+                      struct vuvbm_device *bm_dev);
 int v4l2_dequeue_buffer(int fd, enum v4l2_buf_type type,
                         enum v4l2_memory memory,
                         struct stream *s);
diff --git a/tools/vhost-user-video/vhost-user-video.c b/tools/vhost-user-video/vhost-user-video.c
index cee81c69c3..14c8f42c52 100644
--- a/tools/vhost-user-video/vhost-user-video.c
+++ b/tools/vhost-user-video/vhost-user-video.c
@@ -391,6 +391,9 @@ void remove_all_resources(struct stream *s, uint32_t queue_type)
 
             /* free resource memory allocated in resource_create() */
             g_free(r->iov);
+            if (r->buf != NULL) {
+                vuvbm_buffer_destroy(r->buf);
+            }
             g_free(r);
             *resource_list = g_list_delete_link(*resource_list, l);
         }
@@ -880,10 +883,16 @@ handle_resource_create_cmd(struct VuVideo *v,
         res->iov_count = total_entries;
     } break;
     case VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT:
-        g_critical("%s: VIRTIO_OBJECT not implemented!", __func__);
-        /* TODO implement VIRTIO_OBJECT support */
-        cmd->hdr.type = VIRTIO_VIDEO_RESP_ERR_INVALID_PARAMETER;
-        goto out_unlock;
+    {
+        struct virtio_video_object_entry *ent;
+        ent = (void *)cmd + sizeof(struct virtio_video_resource_create);
+
+        memcpy(&res->uuid, ent->uuid, sizeof(ent->uuid));
+        g_debug("%s: create resource uuid(%s)",
+                __func__, qemu_uuid_unparse_strdup(&res->uuid));
+
+        vuvbm_init_device(v->bm_dev);
+    } break;
     }
 
     cmd->hdr.type = VIRTIO_VIDEO_RESP_OK_NODATA;
@@ -974,7 +983,7 @@ handle_resource_queue_cmd(struct VuVideo *v,
         get_queue_mem_type(s, cmd->queue_type);
     enum v4l2_memory memory = get_v4l2_memory(mem_type);
 
-    ret = v4l2_queue_buffer(buf_type, memory, cmd, res, s, v->v4l2_dev);
+    ret = v4l2_queue_buffer(buf_type, memory, cmd, res, s, v->v4l2_dev, v->bm_dev);
     if (ret < 0) {
         g_critical("%s: v4l2_queue_buffer failed", __func__);
         /* virtio error set by v4l2_queue_buffer */
@@ -1064,12 +1073,7 @@ handle_stream_create_cmd(struct VuVideo *v,
     req_stream_id = cmd->hdr.stream_id;
     coded_format = le32toh(cmd->coded_format);
 
-    if ((le32toh(cmd->in_mem_type) == VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT) ||
-        (le32toh(cmd->out_mem_type) == VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT)) {
-        /* TODO implement VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT */
-        g_printerr("%s: MEM_TYPE_VIRTIO_OBJECT not supported yet", __func__);
-        cmd->hdr.type = VIRTIO_VIDEO_RESP_ERR_INVALID_PARAMETER;
-    } else if (find_stream(v, req_stream_id)) {
+    if (find_stream(v, req_stream_id)) {
         g_debug("%s: Stream ID in use - ", __func__);
         cmd->hdr.type = VIRTIO_VIDEO_RESP_ERR_INVALID_STREAM_ID;
     } else {
@@ -1212,7 +1216,6 @@ handle_stream_destroy_cmd(struct VuVideo *v,
 
         buftype = s->has_mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
             V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
         mem_type = get_queue_mem_type(s, VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
         video_free_buffers(s->fd, buftype, get_v4l2_memory(mem_type));
         remove_all_resources(s, VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
@@ -1644,7 +1647,7 @@ static void video_destroy(VuVideo *v)
     if (socket_path) {
         unlink(socket_path);
     }
-
+    vuvbm_device_destroy(v->bm_dev);
     v4l2_backend_free(v->v4l2_dev);
 }
 
@@ -1717,6 +1720,11 @@ int main(int argc, char *argv[])
         }
     }
 
+    /*
+     * Create a new Buffer Memory device to handle DMA buffers.
+     */
+    video.bm_dev = g_new0(struct vuvbm_device, 1);
+
     /*
      * Now create a vhost-user socket that we will receive messages
      * on. Once we have our handler set up we can enter the glib main
-- 
2.39.2



      parent reply	other threads:[~2023-03-22 14:23 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-22 14:21 [PATCH 00/12] [RFC PATCHv2] Add vhost-user Video decode Albert Esteve
2023-03-22 14:21 ` [PATCH 01/12] docs: Add a vhost-virtio-video rst file Albert Esteve
2023-03-22 14:21 ` [PATCH 02/12] MAINTAINERS: Add virtio-video section Albert Esteve
2023-03-22 14:45   ` Thomas Huth
2023-03-22 15:28     ` Albert Esteve
2023-03-22 14:21 ` [PATCH 03/12] vhost-user-video: boiler plate code for vhost-user-video device Albert Esteve
2023-03-22 14:21 ` [PATCH 04/12] vhost-user-video: add meson subdir build logic Albert Esteve
2023-03-22 14:21 ` [PATCH 05/12] standard-headers: Add virtio_video.h Albert Esteve
2023-03-22 14:21 ` [PATCH 06/12] hw/display: add vhost-user-video-pci Albert Esteve
2023-03-22 14:21 ` [PATCH 07/12] vhost-user.json: add video type Albert Esteve
2023-03-22 14:21 ` [PATCH 08/12] tools/vhost-user-video: Add initial vhost-user-video vmm Albert Esteve
2023-03-22 14:21 ` [PATCH 09/12] tests/qtest: add virtio-video test Albert Esteve
2023-03-22 14:21 ` [PATCH 10/12] vhost-user-video: add dev_type to CLI Albert Esteve
2023-03-22 14:21 ` [PATCH 11/12] vhost-user-video-udmabuf: add udmabuf helpers Albert Esteve
2023-03-22 14:21 ` Albert Esteve [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230322142132.22909-13-aesteve@redhat.com \
    --to=aesteve@redhat.com \
    --cc=alex.bennee@linaro.org \
    --cc=eballetb@redhat.com \
    --cc=fmartine@redhat.com \
    --cc=kraxel@redhat.com \
    --cc=lvivier@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=mst@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.griffin@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=sgarzare@redhat.com \
    --cc=thuth@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).