OSDN Git Service

drm/virtio: implement blob resources: implement vram object
authorGerd Hoffmann <kraxel@redhat.com>
Thu, 24 Sep 2020 00:32:07 +0000 (17:32 -0700)
committerGerd Hoffmann <kraxel@redhat.com>
Tue, 29 Sep 2020 09:23:33 +0000 (11:23 +0200)
A virtio-gpu vram object is based on range-based allocation.
No guest shmemfs backing, so we call drm_gem_private_object_init.

This is for host memory without any guest backing (atleast initially).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20200924003214.662-12-gurchetansingh@chromium.org
Co-developed-by: Gurchetan Singh <gurchetansingh@chromium.org>
Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org>
drivers/gpu/drm/virtio/Makefile
drivers/gpu/drm/virtio/virtgpu_debugfs.c
drivers/gpu/drm/virtio/virtgpu_drv.h
drivers/gpu/drm/virtio/virtgpu_kms.c
drivers/gpu/drm/virtio/virtgpu_object.c
drivers/gpu/drm/virtio/virtgpu_vq.c
drivers/gpu/drm/virtio/virtgpu_vram.c [new file with mode: 0644]

index 92aa2b3..b99fa4a 100644 (file)
@@ -3,7 +3,7 @@
 # Makefile for the drm device driver.  This driver provides support for the
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
-virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_gem.o \
+virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_gem.o virtgpu_vram.o \
        virtgpu_display.o virtgpu_vq.o \
        virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \
        virtgpu_ioctl.o virtgpu_prime.o virtgpu_trace_points.o
index a2cdd26..f336a8f 100644 (file)
@@ -42,7 +42,7 @@ static void virtio_add_int(struct seq_file *m, const char *name,
 
 static int virtio_gpu_features(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = (struct drm_info_node *)m->private;
        struct virtio_gpu_device *vgdev = node->minor->dev->dev_private;
 
        virtio_add_bool(m, "virgl", vgdev->has_virgl_3d);
@@ -72,9 +72,27 @@ virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data)
        return 0;
 }
 
+static int
+virtio_gpu_debugfs_host_visible_mm(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *)m->private;
+       struct virtio_gpu_device *vgdev = node->minor->dev->dev_private;
+       struct drm_printer p;
+
+       if (!vgdev->has_host_visible) {
+               seq_puts(m, "Host allocations not visible to guest\n");
+               return 0;
+       }
+
+       p = drm_seq_file_printer(m);
+       drm_mm_print(&vgdev->host_visible_mm, &p);
+       return 0;
+}
+
 static struct drm_info_list virtio_gpu_debugfs_list[] = {
        { "virtio-gpu-features", virtio_gpu_features },
        { "virtio-gpu-irq-fence", virtio_gpu_debugfs_irq_info, 0, NULL },
+       { "virtio-gpu-host-visible-mm", virtio_gpu_debugfs_host_visible_mm },
 };
 
 #define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list)
index bddf06b..7c9cf69 100644 (file)
@@ -99,9 +99,19 @@ struct virtio_gpu_object_shmem {
        uint32_t mapped;
 };
 
+struct virtio_gpu_object_vram {
+       struct virtio_gpu_object base;
+       uint32_t map_state;
+       uint32_t map_info;
+       struct drm_mm_node vram_node;
+};
+
 #define to_virtio_gpu_shmem(virtio_gpu_object) \
        container_of((virtio_gpu_object), struct virtio_gpu_object_shmem, base)
 
+#define to_virtio_gpu_vram(virtio_gpu_object) \
+       container_of((virtio_gpu_object), struct virtio_gpu_object_vram, base)
+
 struct virtio_gpu_object_array {
        struct ww_acquire_ctx ticket;
        struct list_head next;
@@ -222,6 +232,7 @@ struct virtio_gpu_device {
        bool has_resource_blob;
        bool has_host_visible;
        struct virtio_shm_region host_visible_region;
+       struct drm_mm host_visible_mm;
 
        struct work_struct config_changed_work;
 
@@ -233,8 +244,10 @@ struct virtio_gpu_device {
        uint32_t num_capsets;
        struct list_head cap_cache;
 
-       /* protects resource state when exporting */
+       /* protects uuid state when exporting */
        spinlock_t resource_export_lock;
+       /* protects map state and host_visible_mm */
+       spinlock_t host_visible_lock;
 };
 
 struct virtio_gpu_fpriv {
@@ -365,6 +378,12 @@ int
 virtio_gpu_cmd_resource_assign_uuid(struct virtio_gpu_device *vgdev,
                                    struct virtio_gpu_object_array *objs);
 
+int virtio_gpu_cmd_map(struct virtio_gpu_device *vgdev,
+                      struct virtio_gpu_object_array *objs, uint64_t offset);
+
+void virtio_gpu_cmd_unmap(struct virtio_gpu_device *vgdev,
+                         struct virtio_gpu_object *bo);
+
 /* virtgpu_display.c */
 int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev);
 void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev);
@@ -411,4 +430,9 @@ struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
 /* virtgpu_debugfs.c */
 void virtio_gpu_debugfs_init(struct drm_minor *minor);
 
+/* virtgpu_vram.c */
+bool virtio_gpu_is_vram(struct virtio_gpu_object *bo);
+int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
+                          struct virtio_gpu_object_params *params,
+                          struct virtio_gpu_object **bo_ptr);
 #endif
index e17d3f5..b4ec479 100644 (file)
@@ -121,6 +121,7 @@ int virtio_gpu_init(struct drm_device *dev)
 
        spin_lock_init(&vgdev->display_info_lock);
        spin_lock_init(&vgdev->resource_export_lock);
+       spin_lock_init(&vgdev->host_visible_lock);
        ida_init(&vgdev->ctx_id_ida);
        ida_init(&vgdev->resource_ida);
        init_waitqueue_head(&vgdev->resp_wq);
@@ -169,6 +170,9 @@ int virtio_gpu_init(struct drm_device *dev)
                         (unsigned long)vgdev->host_visible_region.addr,
                         (unsigned long)vgdev->host_visible_region.len);
                vgdev->has_host_visible = true;
+               drm_mm_init(&vgdev->host_visible_mm,
+                           (unsigned long)vgdev->host_visible_region.addr,
+                           (unsigned long)vgdev->host_visible_region.len);
        }
 
        DRM_INFO("features: %cvirgl %cedid %cresource_blob %chost_visible\n",
@@ -262,6 +266,10 @@ void virtio_gpu_release(struct drm_device *dev)
        virtio_gpu_modeset_fini(vgdev);
        virtio_gpu_free_vbufs(vgdev);
        virtio_gpu_cleanup_cap_cache(vgdev);
+
+       if (vgdev->has_host_visible)
+               drm_mm_takedown(&vgdev->host_visible_mm);
+
        kfree(vgdev->capsets);
        kfree(vgdev);
 }
index e397060..6b05a31 100644 (file)
@@ -84,6 +84,18 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
                }
 
                drm_gem_shmem_free_object(&bo->base.base);
+       } else if (virtio_gpu_is_vram(bo)) {
+               struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
+
+               spin_lock(&vgdev->host_visible_lock);
+               if (drm_mm_node_allocated(&vram->vram_node))
+                       drm_mm_remove_node(&vram->vram_node);
+
+               spin_unlock(&vgdev->host_visible_lock);
+
+               drm_gem_free_mmap_offset(&vram->base.base.base);
+               drm_gem_object_release(&vram->base.base.base);
+               kfree(vram);
        }
 }
 
index 55529ed..41fd253 100644 (file)
@@ -1171,3 +1171,69 @@ virtio_gpu_cmd_resource_assign_uuid(struct virtio_gpu_device *vgdev,
        virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
        return 0;
 }
+
+static void virtio_gpu_cmd_resource_map_cb(struct virtio_gpu_device *vgdev,
+                                          struct virtio_gpu_vbuffer *vbuf)
+{
+       struct virtio_gpu_object *bo =
+               gem_to_virtio_gpu_obj(vbuf->objs->objs[0]);
+       struct virtio_gpu_resp_map_info *resp =
+               (struct virtio_gpu_resp_map_info *)vbuf->resp_buf;
+       struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
+       uint32_t resp_type = le32_to_cpu(resp->hdr.type);
+
+       spin_lock(&vgdev->host_visible_lock);
+
+       if (resp_type == VIRTIO_GPU_RESP_OK_MAP_INFO) {
+               vram->map_info = resp->map_info;
+               vram->map_state = STATE_OK;
+       } else {
+               vram->map_state = STATE_ERR;
+       }
+
+       spin_unlock(&vgdev->host_visible_lock);
+       wake_up_all(&vgdev->resp_wq);
+}
+
+int virtio_gpu_cmd_map(struct virtio_gpu_device *vgdev,
+                      struct virtio_gpu_object_array *objs, uint64_t offset)
+{
+       struct virtio_gpu_resource_map_blob *cmd_p;
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
+       struct virtio_gpu_vbuffer *vbuf;
+       struct virtio_gpu_resp_map_info *resp_buf;
+
+       resp_buf = kzalloc(sizeof(*resp_buf), GFP_KERNEL);
+       if (!resp_buf) {
+               virtio_gpu_array_put_free(objs);
+               return -ENOMEM;
+       }
+
+       cmd_p = virtio_gpu_alloc_cmd_resp
+               (vgdev, virtio_gpu_cmd_resource_map_cb, &vbuf, sizeof(*cmd_p),
+                sizeof(struct virtio_gpu_resp_map_info), resp_buf);
+       memset(cmd_p, 0, sizeof(*cmd_p));
+
+       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB);
+       cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);
+       cmd_p->offset = cpu_to_le64(offset);
+       vbuf->objs = objs;
+
+       virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+       return 0;
+}
+
+void virtio_gpu_cmd_unmap(struct virtio_gpu_device *vgdev,
+                         struct virtio_gpu_object *bo)
+{
+       struct virtio_gpu_resource_unmap_blob *cmd_p;
+       struct virtio_gpu_vbuffer *vbuf;
+
+       cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+       memset(cmd_p, 0, sizeof(*cmd_p));
+
+       cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB);
+       cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);
+
+       virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vram.c b/drivers/gpu/drm/virtio/virtgpu_vram.c
new file mode 100644 (file)
index 0000000..087945f
--- /dev/null
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "virtgpu_drv.h"
+
+static void virtio_gpu_vram_free(struct drm_gem_object *obj)
+{
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
+       struct virtio_gpu_device *vgdev = obj->dev->dev_private;
+       struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
+       bool unmap;
+
+       if (bo->created) {
+               spin_lock(&vgdev->host_visible_lock);
+               unmap = drm_mm_node_allocated(&vram->vram_node);
+               spin_unlock(&vgdev->host_visible_lock);
+
+               if (unmap)
+                       virtio_gpu_cmd_unmap(vgdev, bo);
+
+               virtio_gpu_cmd_unref_resource(vgdev, bo);
+               virtio_gpu_notify(vgdev);
+               return;
+       }
+}
+
+static const struct vm_operations_struct virtio_gpu_vram_vm_ops = {
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
+static int virtio_gpu_vram_mmap(struct drm_gem_object *obj,
+                               struct vm_area_struct *vma)
+{
+       int ret;
+       struct virtio_gpu_device *vgdev = obj->dev->dev_private;
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
+       struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
+       unsigned long vm_size = vma->vm_end - vma->vm_start;
+
+       if (!(bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE))
+               return -EINVAL;
+
+       wait_event(vgdev->resp_wq, vram->map_state != STATE_INITIALIZING);
+       if (vram->map_state != STATE_OK)
+               return -EINVAL;
+
+       vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node);
+       vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND;
+       vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+       vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+       vma->vm_ops = &virtio_gpu_vram_vm_ops;
+
+       if (vram->map_info == VIRTIO_GPU_MAP_CACHE_WC)
+               vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+       else if (vram->map_info == VIRTIO_GPU_MAP_CACHE_UNCACHED)
+               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       /* Partial mappings of GEM buffers don't happen much in practice. */
+       if (vm_size != vram->vram_node.size)
+               return -EINVAL;
+
+       ret = io_remap_pfn_range(vma, vma->vm_start,
+                                vram->vram_node.start >> PAGE_SHIFT,
+                                vm_size, vma->vm_page_prot);
+       return ret;
+}
+
+static const struct drm_gem_object_funcs virtio_gpu_vram_funcs = {
+       .open = virtio_gpu_gem_object_open,
+       .close = virtio_gpu_gem_object_close,
+       .free = virtio_gpu_vram_free,
+       .mmap = virtio_gpu_vram_mmap,
+};
+
+bool virtio_gpu_is_vram(struct virtio_gpu_object *bo)
+{
+       return bo->base.base.funcs == &virtio_gpu_vram_funcs;
+}
+
+static int virtio_gpu_vram_map(struct virtio_gpu_object *bo)
+{
+       int ret;
+       uint64_t offset;
+       struct virtio_gpu_object_array *objs;
+       struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
+       struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
+
+       if (!vgdev->has_host_visible)
+               return -EINVAL;
+
+       spin_lock(&vgdev->host_visible_lock);
+       ret = drm_mm_insert_node(&vgdev->host_visible_mm, &vram->vram_node,
+                                bo->base.base.size);
+       spin_unlock(&vgdev->host_visible_lock);
+
+       if (ret)
+               return ret;
+
+       objs = virtio_gpu_array_alloc(1);
+       if (!objs) {
+               ret = -ENOMEM;
+               goto err_remove_node;
+       }
+
+       virtio_gpu_array_add_obj(objs, &bo->base.base);
+       /*TODO: Add an error checking helper function in drm_mm.h */
+       offset = vram->vram_node.start - vgdev->host_visible_region.addr;
+
+       ret = virtio_gpu_cmd_map(vgdev, objs, offset);
+       if (ret) {
+               virtio_gpu_array_put_free(objs);
+               goto err_remove_node;
+       }
+
+       return 0;
+
+err_remove_node:
+       spin_lock(&vgdev->host_visible_lock);
+       drm_mm_remove_node(&vram->vram_node);
+       spin_unlock(&vgdev->host_visible_lock);
+       return ret;
+}
+
+int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
+                          struct virtio_gpu_object_params *params,
+                          struct virtio_gpu_object **bo_ptr)
+{
+       struct drm_gem_object *obj;
+       struct virtio_gpu_object_vram *vram;
+       int ret;
+
+       vram = kzalloc(sizeof(*vram), GFP_KERNEL);
+       if (!vram)
+               return -ENOMEM;
+
+       obj = &vram->base.base.base;
+       obj->funcs = &virtio_gpu_vram_funcs;
+       drm_gem_private_object_init(vgdev->ddev, obj, params->size);
+
+       /* Create fake offset */
+       ret = drm_gem_create_mmap_offset(obj);
+       if (ret) {
+               kfree(vram);
+               return ret;
+       }
+
+       ret = virtio_gpu_resource_id_get(vgdev, &vram->base.hw_res_handle);
+       if (ret) {
+               kfree(vram);
+               return ret;
+       }
+
+       if (params->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE) {
+               ret = virtio_gpu_vram_map(&vram->base);
+               if (ret) {
+                       virtio_gpu_vram_free(obj);
+                       return ret;
+               }
+       }
+
+       *bo_ptr = &vram->base;
+       return 0;
+}