drm/vkms: Add dumb operations
authorRodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
Thu, 12 Jul 2018 02:01:47 +0000 (23:01 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 12 Jul 2018 06:47:44 +0000 (08:47 +0200)
VKMS currently does not handle dumb data, and as a consequence, it does
not provide mechanisms for handling gem. This commit adds the necessary
support for gem object/handler and the dumb functions.

Changes since V1:
 Daniel Vetter:
 - Add dumb buffer support to the same patchset
Changes since V2:
 Haneen:
 - Add missing gem_free_object_unlocked callback to fix the warning
   "Memory manager not clean during takedown"

Signed-off-by: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/70b7becc91c6a323dbc15cb5fc912cbdfe4ef7d9.1531359228.git.rodrigosiqueiramelo@gmail.com
drivers/gpu/drm/vkms/Makefile
drivers/gpu/drm/vkms/vkms_drv.c
drivers/gpu/drm/vkms/vkms_drv.h
drivers/gpu/drm/vkms/vkms_gem.c [new file with mode: 0644]

index 3f774a6a9c58177e8eb85c7f7eb81e2adc30a8cc..986297da51bff0054015faebfff5981c81dced03 100644 (file)
@@ -1,3 +1,3 @@
-vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o
+vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o vkms_gem.o
 
 obj-$(CONFIG_DRM_VKMS) += vkms.o
index 740a4cbfed91c8554d61054af4ea1d20960a5389..6ea2fd97bef9494601e08d1e2790c205963afff5 100644 (file)
@@ -37,6 +37,12 @@ static const struct file_operations vkms_driver_fops = {
        .release        = drm_release,
 };
 
+static const struct vm_operations_struct vkms_gem_vm_ops = {
+       .fault = vkms_gem_fault,
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
 static void vkms_release(struct drm_device *dev)
 {
        struct vkms_device *vkms = container_of(dev, struct vkms_device, drm);
@@ -50,6 +56,10 @@ static struct drm_driver vkms_driver = {
        .driver_features        = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
        .release                = vkms_release,
        .fops                   = &vkms_driver_fops,
+       .dumb_create            = vkms_dumb_create,
+       .dumb_map_offset        = vkms_dumb_map,
+       .gem_vm_ops             = &vkms_gem_vm_ops,
+       .gem_free_object_unlocked = vkms_gem_free_object,
 
        .name                   = DRIVER_NAME,
        .desc                   = DRIVER_DESC,
index b0f9d2e61a4221edd2814fc3327ff86564f178d7..cce4694cafb94ee6176f8d9935c83dcfa230f95c 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm.h>
+#include <drm/drm_gem.h>
 #include <drm/drm_encoder.h>
 
 static const u32 vkms_formats[] = {
@@ -21,6 +22,12 @@ struct vkms_device {
        struct vkms_output output;
 };
 
+struct vkms_gem_object {
+       struct drm_gem_object gem;
+       struct mutex pages_lock; /* Page lock used in page fault handler */
+       struct page **pages;
+};
+
 int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
                   struct drm_plane *primary, struct drm_plane *cursor);
 
@@ -28,4 +35,20 @@ int vkms_output_init(struct vkms_device *vkmsdev);
 
 struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev);
 
+/* Gem stuff */
+struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
+                                      struct drm_file *file,
+                                      u32 *handle,
+                                      u64 size);
+
+int vkms_gem_fault(struct vm_fault *vmf);
+
+int vkms_dumb_create(struct drm_file *file, struct drm_device *dev,
+                    struct drm_mode_create_dumb *args);
+
+int vkms_dumb_map(struct drm_file *file, struct drm_device *dev,
+                 u32 handle, u64 *offset);
+
+void vkms_gem_free_object(struct drm_gem_object *obj);
+
 #endif /* _VKMS_DRV_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_gem.c b/drivers/gpu/drm/vkms/vkms_gem.c
new file mode 100644 (file)
index 0000000..c7e3836
--- /dev/null
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/shmem_fs.h>
+
+#include "vkms_drv.h"
+
+static struct vkms_gem_object *__vkms_gem_create(struct drm_device *dev,
+                                                u64 size)
+{
+       struct vkms_gem_object *obj;
+       int ret;
+
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               return ERR_PTR(-ENOMEM);
+
+       size = roundup(size, PAGE_SIZE);
+       ret = drm_gem_object_init(dev, &obj->gem, size);
+       if (ret) {
+               kfree(obj);
+               return ERR_PTR(ret);
+       }
+
+       mutex_init(&obj->pages_lock);
+
+       return obj;
+}
+
+void vkms_gem_free_object(struct drm_gem_object *obj)
+{
+       struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object,
+                                                  gem);
+
+       kvfree(gem->pages);
+       mutex_destroy(&gem->pages_lock);
+       drm_gem_object_release(obj);
+       kfree(gem);
+}
+
+int vkms_gem_fault(struct vm_fault *vmf)
+{
+       struct vm_area_struct *vma = vmf->vma;
+       struct vkms_gem_object *obj = vma->vm_private_data;
+       unsigned long vaddr = vmf->address;
+       pgoff_t page_offset;
+       loff_t num_pages;
+       int ret;
+
+       page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
+       num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE);
+
+       if (page_offset > num_pages)
+               return VM_FAULT_SIGBUS;
+
+       ret = -ENOENT;
+       mutex_lock(&obj->pages_lock);
+       if (obj->pages) {
+               get_page(obj->pages[page_offset]);
+               vmf->page = obj->pages[page_offset];
+               ret = 0;
+       }
+       mutex_unlock(&obj->pages_lock);
+       if (ret) {
+               struct page *page;
+               struct address_space *mapping;
+
+               mapping = file_inode(obj->gem.filp)->i_mapping;
+               page = shmem_read_mapping_page(mapping, page_offset);
+
+               if (!IS_ERR(page)) {
+                       vmf->page = page;
+                       ret = 0;
+               } else {
+                       switch (PTR_ERR(page)) {
+                       case -ENOSPC:
+                       case -ENOMEM:
+                               ret = VM_FAULT_OOM;
+                               break;
+                       case -EBUSY:
+                               ret = VM_FAULT_RETRY;
+                               break;
+                       case -EFAULT:
+                       case -EINVAL:
+                               ret = VM_FAULT_SIGBUS;
+                               break;
+                       default:
+                               WARN_ON(PTR_ERR(page));
+                               ret = VM_FAULT_SIGBUS;
+                               break;
+                       }
+               }
+       }
+       return ret;
+}
+
+struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
+                                      struct drm_file *file,
+                                      u32 *handle,
+                                      u64 size)
+{
+       struct vkms_gem_object *obj;
+       int ret;
+
+       if (!file || !dev || !handle)
+               return ERR_PTR(-EINVAL);
+
+       obj = __vkms_gem_create(dev, size);
+       if (IS_ERR(obj))
+               return ERR_CAST(obj);
+
+       ret = drm_gem_handle_create(file, &obj->gem, handle);
+       drm_gem_object_put_unlocked(&obj->gem);
+       if (ret) {
+               drm_gem_object_release(&obj->gem);
+               kfree(obj);
+               return ERR_PTR(ret);
+       }
+
+       return &obj->gem;
+}
+
+int vkms_dumb_create(struct drm_file *file, struct drm_device *dev,
+                    struct drm_mode_create_dumb *args)
+{
+       struct drm_gem_object *gem_obj;
+       u64 pitch, size;
+
+       if (!args || !dev || !file)
+               return -EINVAL;
+
+       pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
+       size = pitch * args->height;
+
+       if (!size)
+               return -EINVAL;
+
+       gem_obj = vkms_gem_create(dev, file, &args->handle, size);
+       if (IS_ERR(gem_obj))
+               return PTR_ERR(gem_obj);
+
+       args->size = gem_obj->size;
+       args->pitch = pitch;
+
+       DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
+
+       return 0;
+}
+
+int vkms_dumb_map(struct drm_file *file, struct drm_device *dev,
+                 u32 handle, u64 *offset)
+{
+       struct drm_gem_object *obj;
+       int ret;
+
+       obj = drm_gem_object_lookup(file, handle);
+       if (!obj)
+               return -ENOENT;
+
+       if (!obj->filp) {
+               ret = -EINVAL;
+               goto unref;
+       }
+
+       ret = drm_gem_create_mmap_offset(obj);
+       if (ret)
+               goto unref;
+
+       *offset = drm_vma_node_offset_addr(&obj->vma_node);
+unref:
+       drm_gem_object_put_unlocked(obj);
+
+       return ret;
+}