OSDN Git Service

drm/lima: support heap buffer creation
authorQiang Yu <yuq825@gmail.com>
Thu, 16 Jan 2020 13:11:55 +0000 (21:11 +0800)
committerQiang Yu <yuq825@gmail.com>
Mon, 27 Jan 2020 14:01:09 +0000 (22:01 +0800)
heap buffer is used as output of GP and input of PP for
Mali Utgard GPU. Size of heap buffer depends on the task
so is a runtime variable.

Previously we just create a large enough buffer as heap
buffer. Now we add a heap buffer type to be able to
increase the backup memory dynamically when GP fail due
to lack of heap memory.

Reviewed-by: Vasily Khoruzhick <anarsoul@gmail.com>
Tested-by: Andreas Baierl <ichgeh@imkreisrum.de>
Signed-off-by: Qiang Yu <yuq825@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200116131157.13346-4-yuq825@gmail.com
drivers/gpu/drm/lima/lima_drv.c
drivers/gpu/drm/lima/lima_drv.h
drivers/gpu/drm/lima/lima_gem.c
drivers/gpu/drm/lima/lima_gem.h
drivers/gpu/drm/lima/lima_vm.c
include/uapi/drm/lima_drm.h

index 124efe4..18f88aa 100644 (file)
 #include "lima_vm.h"
 
 int lima_sched_timeout_ms;
+uint lima_heap_init_nr_pages = 8;
 
 MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms");
 module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444);
 
+MODULE_PARM_DESC(heap_init_nr_pages, "heap buffer init number of pages");
+module_param_named(heap_init_nr_pages, lima_heap_init_nr_pages, uint, 0444);
+
 static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file)
 {
        struct drm_lima_get_param *args = data;
@@ -68,7 +72,7 @@ static int lima_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_
        if (args->pad)
                return -EINVAL;
 
-       if (args->flags)
+       if (args->flags & ~(LIMA_BO_FLAG_HEAP))
                return -EINVAL;
 
        if (args->size == 0)
index 69c7344..f492ecc 100644 (file)
@@ -9,6 +9,7 @@
 #include "lima_ctx.h"
 
 extern int lima_sched_timeout_ms;
+extern uint lima_heap_init_nr_pages;
 
 struct lima_vm;
 struct lima_bo;
index d0059d8..5404e0d 100644 (file)
@@ -4,6 +4,8 @@
 #include <linux/mm.h>
 #include <linux/sync_file.h>
 #include <linux/pagemap.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-mapping.h>
 
 #include <drm/drm_file.h>
 #include <drm/drm_syncobj.h>
 #include "lima_gem.h"
 #include "lima_vm.h"
 
+int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm)
+{
+       struct page **pages;
+       struct address_space *mapping = bo->base.base.filp->f_mapping;
+       struct device *dev = bo->base.base.dev->dev;
+       size_t old_size = bo->heap_size;
+       size_t new_size = bo->heap_size ? bo->heap_size * 2 :
+               (lima_heap_init_nr_pages << PAGE_SHIFT);
+       struct sg_table sgt;
+       int i, ret;
+
+       if (bo->heap_size >= bo->base.base.size)
+               return -ENOSPC;
+
+       new_size = min(new_size, bo->base.base.size);
+
+       mutex_lock(&bo->base.pages_lock);
+
+       if (bo->base.pages) {
+               pages = bo->base.pages;
+       } else {
+               pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT,
+                                      sizeof(*pages), GFP_KERNEL | __GFP_ZERO);
+               if (!pages) {
+                       mutex_unlock(&bo->base.pages_lock);
+                       return -ENOMEM;
+               }
+
+               bo->base.pages = pages;
+               bo->base.pages_use_count = 1;
+
+               mapping_set_unevictable(mapping);
+       }
+
+       for (i = old_size >> PAGE_SHIFT; i < new_size >> PAGE_SHIFT; i++) {
+               struct page *page = shmem_read_mapping_page(mapping, i);
+
+               if (IS_ERR(page)) {
+                       mutex_unlock(&bo->base.pages_lock);
+                       return PTR_ERR(page);
+               }
+               pages[i] = page;
+       }
+
+       mutex_unlock(&bo->base.pages_lock);
+
+       ret = sg_alloc_table_from_pages(&sgt, pages, i, 0,
+                                       new_size, GFP_KERNEL);
+       if (ret)
+               return ret;
+
+       if (bo->base.sgt) {
+               dma_unmap_sg(dev, bo->base.sgt->sgl,
+                            bo->base.sgt->nents, DMA_BIDIRECTIONAL);
+               sg_free_table(bo->base.sgt);
+       } else {
+               bo->base.sgt = kmalloc(sizeof(*bo->base.sgt), GFP_KERNEL);
+               if (!bo->base.sgt) {
+                       sg_free_table(&sgt);
+                       return -ENOMEM;
+               }
+       }
+
+       dma_map_sg(dev, sgt.sgl, sgt.nents, DMA_BIDIRECTIONAL);
+
+       *bo->base.sgt = sgt;
+
+       if (vm) {
+               ret = lima_vm_map_bo(vm, bo, old_size >> PAGE_SHIFT);
+               if (ret)
+                       return ret;
+       }
+
+       bo->heap_size = new_size;
+       return 0;
+}
+
 int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
                           u32 size, u32 flags, u32 *handle)
 {
@@ -22,7 +101,8 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
        gfp_t mask;
        struct drm_gem_shmem_object *shmem;
        struct drm_gem_object *obj;
-       struct sg_table *sgt;
+       struct lima_bo *bo;
+       bool is_heap = flags & LIMA_BO_FLAG_HEAP;
 
        shmem = drm_gem_shmem_create(dev, size);
        if (IS_ERR(shmem))
@@ -36,10 +116,18 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
        mask |= __GFP_DMA32;
        mapping_set_gfp_mask(obj->filp->f_mapping, mask);
 
-       sgt = drm_gem_shmem_get_pages_sgt(obj);
-       if (IS_ERR(sgt)) {
-               err = PTR_ERR(sgt);
-               goto out;
+       if (is_heap) {
+               bo = to_lima_bo(obj);
+               err = lima_heap_alloc(bo, NULL);
+               if (err)
+                       goto out;
+       } else {
+               struct sg_table *sgt = drm_gem_shmem_get_pages_sgt(obj);
+
+               if (IS_ERR(sgt)) {
+                       err = PTR_ERR(sgt);
+                       goto out;
+               }
        }
 
        err = drm_gem_handle_create(file, obj, handle);
@@ -79,17 +167,47 @@ static void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *f
        lima_vm_bo_del(vm, bo);
 }
 
+static int lima_gem_pin(struct drm_gem_object *obj)
+{
+       struct lima_bo *bo = to_lima_bo(obj);
+
+       if (bo->heap_size)
+               return -EINVAL;
+
+       return drm_gem_shmem_pin(obj);
+}
+
+static void *lima_gem_vmap(struct drm_gem_object *obj)
+{
+       struct lima_bo *bo = to_lima_bo(obj);
+
+       if (bo->heap_size)
+               return ERR_PTR(-EINVAL);
+
+       return drm_gem_shmem_vmap(obj);
+}
+
+static int lima_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+       struct lima_bo *bo = to_lima_bo(obj);
+
+       if (bo->heap_size)
+               return -EINVAL;
+
+       return drm_gem_shmem_mmap(obj, vma);
+}
+
 static const struct drm_gem_object_funcs lima_gem_funcs = {
        .free = lima_gem_free_object,
        .open = lima_gem_object_open,
        .close = lima_gem_object_close,
        .print_info = drm_gem_shmem_print_info,
-       .pin = drm_gem_shmem_pin,
+       .pin = lima_gem_pin,
        .unpin = drm_gem_shmem_unpin,
        .get_sg_table = drm_gem_shmem_get_sg_table,
-       .vmap = drm_gem_shmem_vmap,
+       .vmap = lima_gem_vmap,
        .vunmap = drm_gem_shmem_vunmap,
-       .mmap = drm_gem_shmem_mmap,
+       .mmap = lima_gem_mmap,
 };
 
 struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size)
index 1800feb..ccea061 100644 (file)
@@ -7,12 +7,15 @@
 #include <drm/drm_gem_shmem_helper.h>
 
 struct lima_submit;
+struct lima_vm;
 
 struct lima_bo {
        struct drm_gem_shmem_object base;
 
        struct mutex lock;
        struct list_head va;
+
+       size_t heap_size;
 };
 
 static inline struct lima_bo *
@@ -31,6 +34,7 @@ static inline struct dma_resv *lima_bo_resv(struct lima_bo *bo)
        return bo->base.base.resv;
 }
 
+int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm);
 struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size);
 int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
                           u32 size, u32 flags, u32 *handle);
index 2e51384..5b92fb8 100644 (file)
@@ -155,6 +155,7 @@ err_out0:
 void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo)
 {
        struct lima_bo_va *bo_va;
+       u32 size;
 
        mutex_lock(&bo->lock);
 
@@ -166,8 +167,9 @@ void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo)
 
        mutex_lock(&vm->lock);
 
+       size = bo->heap_size ? bo->heap_size : bo_va->node.size;
        lima_vm_unmap_range(vm, bo_va->node.start,
-                           bo_va->node.start + bo_va->node.size - 1);
+                           bo_va->node.start + size - 1);
 
        drm_mm_remove_node(&bo_va->node);
 
index 95a00fb..1ec58d6 100644 (file)
@@ -32,12 +32,19 @@ struct drm_lima_get_param {
        __u64 value; /* out, parameter value */
 };
 
+/*
+ * heap buffer dynamically increase backup memory size when GP task fail
+ * due to lack of heap memory. size field of heap buffer is an up bound of
+ * the backup memory which can be set to a fairly large value.
+ */
+#define LIMA_BO_FLAG_HEAP  (1 << 0)
+
 /**
  * create a buffer for used by GPU
  */
 struct drm_lima_gem_create {
        __u32 size;    /* in, buffer size */
-       __u32 flags;   /* in, currently no flags, must be zero */
+       __u32 flags;   /* in, buffer flags */
        __u32 handle;  /* out, GEM buffer handle */
        __u32 pad;     /* pad, must be zero */
 };