Commit 6aebc51d authored by Qiang Yu's avatar Qiang Yu

drm/lima: support heap buffer creation

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: default avatarVasily Khoruzhick <anarsoul@gmail.com>
Tested-by: default avatarAndreas Baierl <ichgeh@imkreisrum.de>
Signed-off-by: default avatarQiang Yu <yuq825@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200116131157.13346-4-yuq825@gmail.com
parent dc76cb7a
...@@ -15,10 +15,14 @@ ...@@ -15,10 +15,14 @@
#include "lima_vm.h" #include "lima_vm.h"
int lima_sched_timeout_ms; int lima_sched_timeout_ms;
uint lima_heap_init_nr_pages = 8;
MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms"); MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms");
module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444); 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) static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file)
{ {
struct drm_lima_get_param *args = data; 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_ ...@@ -68,7 +72,7 @@ static int lima_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_
if (args->pad) if (args->pad)
return -EINVAL; return -EINVAL;
if (args->flags) if (args->flags & ~(LIMA_BO_FLAG_HEAP))
return -EINVAL; return -EINVAL;
if (args->size == 0) if (args->size == 0)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "lima_ctx.h" #include "lima_ctx.h"
extern int lima_sched_timeout_ms; extern int lima_sched_timeout_ms;
extern uint lima_heap_init_nr_pages;
struct lima_vm; struct lima_vm;
struct lima_bo; struct lima_bo;
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/sync_file.h> #include <linux/sync_file.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/shmem_fs.h>
#include <linux/dma-mapping.h>
#include <drm/drm_file.h> #include <drm/drm_file.h>
#include <drm/drm_syncobj.h> #include <drm/drm_syncobj.h>
...@@ -15,6 +17,83 @@ ...@@ -15,6 +17,83 @@
#include "lima_gem.h" #include "lima_gem.h"
#include "lima_vm.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, int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
u32 size, u32 flags, u32 *handle) u32 size, u32 flags, u32 *handle)
{ {
...@@ -22,7 +101,8 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, ...@@ -22,7 +101,8 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
gfp_t mask; gfp_t mask;
struct drm_gem_shmem_object *shmem; struct drm_gem_shmem_object *shmem;
struct drm_gem_object *obj; 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); shmem = drm_gem_shmem_create(dev, size);
if (IS_ERR(shmem)) if (IS_ERR(shmem))
...@@ -36,10 +116,18 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, ...@@ -36,10 +116,18 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
mask |= __GFP_DMA32; mask |= __GFP_DMA32;
mapping_set_gfp_mask(obj->filp->f_mapping, mask); mapping_set_gfp_mask(obj->filp->f_mapping, mask);
sgt = drm_gem_shmem_get_pages_sgt(obj); if (is_heap) {
if (IS_ERR(sgt)) { bo = to_lima_bo(obj);
err = PTR_ERR(sgt); err = lima_heap_alloc(bo, NULL);
goto out; 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); 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 ...@@ -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); 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 = { static const struct drm_gem_object_funcs lima_gem_funcs = {
.free = lima_gem_free_object, .free = lima_gem_free_object,
.open = lima_gem_object_open, .open = lima_gem_object_open,
.close = lima_gem_object_close, .close = lima_gem_object_close,
.print_info = drm_gem_shmem_print_info, .print_info = drm_gem_shmem_print_info,
.pin = drm_gem_shmem_pin, .pin = lima_gem_pin,
.unpin = drm_gem_shmem_unpin, .unpin = drm_gem_shmem_unpin,
.get_sg_table = drm_gem_shmem_get_sg_table, .get_sg_table = drm_gem_shmem_get_sg_table,
.vmap = drm_gem_shmem_vmap, .vmap = lima_gem_vmap,
.vunmap = drm_gem_shmem_vunmap, .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) struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size)
......
...@@ -7,12 +7,15 @@ ...@@ -7,12 +7,15 @@
#include <drm/drm_gem_shmem_helper.h> #include <drm/drm_gem_shmem_helper.h>
struct lima_submit; struct lima_submit;
struct lima_vm;
struct lima_bo { struct lima_bo {
struct drm_gem_shmem_object base; struct drm_gem_shmem_object base;
struct mutex lock; struct mutex lock;
struct list_head va; struct list_head va;
size_t heap_size;
}; };
static inline struct lima_bo * static inline struct lima_bo *
...@@ -31,6 +34,7 @@ static inline struct dma_resv *lima_bo_resv(struct lima_bo *bo) ...@@ -31,6 +34,7 @@ static inline struct dma_resv *lima_bo_resv(struct lima_bo *bo)
return bo->base.base.resv; 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); 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, int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
u32 size, u32 flags, u32 *handle); u32 size, u32 flags, u32 *handle);
......
...@@ -155,6 +155,7 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create) ...@@ -155,6 +155,7 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create)
void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo) void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo)
{ {
struct lima_bo_va *bo_va; struct lima_bo_va *bo_va;
u32 size;
mutex_lock(&bo->lock); mutex_lock(&bo->lock);
...@@ -166,8 +167,9 @@ void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo) ...@@ -166,8 +167,9 @@ void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo)
mutex_lock(&vm->lock); mutex_lock(&vm->lock);
size = bo->heap_size ? bo->heap_size : bo_va->node.size;
lima_vm_unmap_range(vm, bo_va->node.start, 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); drm_mm_remove_node(&bo_va->node);
......
...@@ -32,12 +32,19 @@ struct drm_lima_get_param { ...@@ -32,12 +32,19 @@ struct drm_lima_get_param {
__u64 value; /* out, parameter value */ __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 * create a buffer for used by GPU
*/ */
struct drm_lima_gem_create { struct drm_lima_gem_create {
__u32 size; /* in, buffer size */ __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 handle; /* out, GEM buffer handle */
__u32 pad; /* pad, must be zero */ __u32 pad; /* pad, must be zero */
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment