msm: kgsl: Offload mementry destroy work to separate thread

Any memory free ioctl doesn't need to be blocked till the
corresponding mementry is destroyed. This change defers
the mementry put to unblock all memory free ioctls immediately.
This is done to reduce the time spent by user applications in
waiting for memory to be freed.

Change-Id: If081fec0c34549071a61639249fe86924745eb9c
Signed-off-by: Deepak Kumar <dkumar@codeaurora.org>
Signed-off-by: celtare21 <celtare21@gmail.com>
Signed-off-by: azrim <mirzaspc@gmail.com>
This commit is contained in:
Deepak Kumar 2019-11-20 19:48:48 +05:30 committed by azrim
parent ea753c9393
commit b074df3b66
No known key found for this signature in database
GPG Key ID: 497F8FB059B45D1C
6 changed files with 112 additions and 36 deletions

View File

@ -816,7 +816,7 @@ void a6xx_preemption_context_destroy(struct kgsl_context *context)
gpumem_free_entry(context->user_ctxt_record);
/* Put the extra ref from gpumem_alloc_entry() */
kgsl_mem_entry_put(context->user_ctxt_record);
kgsl_mem_entry_put_deferred(context->user_ctxt_record);
}
int a6xx_preemption_context_init(struct kgsl_context *context)

View File

@ -261,15 +261,6 @@ int kgsl_readtimestamp(struct kgsl_device *device, void *priv,
}
EXPORT_SYMBOL(kgsl_readtimestamp);
/* Scheduled by kgsl_mem_entry_put_deferred() */
static void _deferred_put(struct work_struct *work)
{
struct kgsl_mem_entry *entry =
container_of(work, struct kgsl_mem_entry, work);
kgsl_mem_entry_put(entry);
}
static inline struct kgsl_mem_entry *
kgsl_mem_entry_create(void)
{
@ -392,17 +383,10 @@ static void kgsl_destroy_anon(struct kgsl_memdesc *memdesc)
}
}
void
kgsl_mem_entry_destroy(struct kref *kref)
static void mem_entry_destroy(struct kgsl_mem_entry *entry)
{
struct kgsl_mem_entry *entry = container_of(kref,
struct kgsl_mem_entry,
refcount);
unsigned int memtype;
if (entry == NULL)
return;
/* pull out the memtype before the flags get cleared */
memtype = kgsl_memdesc_usermem_type(&entry->memdesc);
@ -421,8 +405,34 @@ kgsl_mem_entry_destroy(struct kref *kref)
kfree(entry);
}
static void _deferred_destroy(struct work_struct *work)
{
struct kgsl_mem_entry *entry =
container_of(work, struct kgsl_mem_entry, work);
mem_entry_destroy(entry);
}
void kgsl_mem_entry_destroy(struct kref *kref)
{
struct kgsl_mem_entry *entry =
container_of(kref, struct kgsl_mem_entry, refcount);
mem_entry_destroy(entry);
}
EXPORT_SYMBOL(kgsl_mem_entry_destroy);
void kgsl_mem_entry_destroy_deferred(struct kref *kref)
{
struct kgsl_mem_entry *entry =
container_of(kref, struct kgsl_mem_entry, refcount);
INIT_WORK(&entry->work, _deferred_destroy);
queue_work(kgsl_driver.mem_workqueue, &entry->work);
}
EXPORT_SYMBOL(kgsl_mem_entry_destroy_deferred);
/* Allocate a IOVA for memory objects that don't use SVM */
static int kgsl_mem_entry_track_gpuaddr(struct kgsl_device *device,
struct kgsl_process_private *process,
@ -1019,6 +1029,13 @@ static struct kgsl_process_private *kgsl_process_private_new(
struct kgsl_process_private *private;
struct pid *cur_pid = get_task_pid(current->group_leader, PIDTYPE_PID);
/*
* Flush mem_workqueue to make sure that any lingering
* structs (process pagetable etc) are released before
* starting over again.
*/
flush_workqueue(kgsl_driver.mem_workqueue);
/* Search in the process list */
list_for_each_entry(private, &kgsl_driver.process_list, list) {
if (private->pid == cur_pid) {
@ -1092,7 +1109,7 @@ static void process_release_memory(struct kgsl_process_private *private)
if (!entry->pending_free) {
entry->pending_free = 1;
spin_unlock(&private->mem_lock);
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
} else {
spin_unlock(&private->mem_lock);
}
@ -2126,7 +2143,7 @@ long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv,
return -EINVAL;
ret = gpumem_free_entry(entry);
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
return ret;
}
@ -2144,7 +2161,7 @@ long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
return -EINVAL;
ret = gpumem_free_entry(entry);
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
return ret;
}
@ -2188,8 +2205,7 @@ static bool gpuobj_free_fence_func(void *priv)
entry->memdesc.gpuaddr, entry->memdesc.size,
entry->memdesc.flags);
INIT_WORK(&entry->work, _deferred_put);
queue_work(kgsl_driver.mem_workqueue, &entry->work);
kgsl_mem_entry_put_deferred(entry);
return true;
}
@ -2254,7 +2270,7 @@ long kgsl_ioctl_gpuobj_free(struct kgsl_device_private *dev_priv,
else
ret = -EINVAL;
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
return ret;
}
@ -2284,7 +2300,7 @@ long kgsl_ioctl_cmdstream_freememontimestamp_ctxtid(
ret = gpumem_free_entry_on_timestamp(dev_priv->device, entry,
context, param->timestamp);
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
kgsl_context_put(context);
return ret;
@ -3664,7 +3680,7 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv,
/* One put for find_id(), one put for the kgsl_mem_entry_create() */
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
return 0;
}
@ -3748,7 +3764,7 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv,
/* One put for find_id(), one put for the kgsl_mem_entry_create() */
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put(entry);
kgsl_mem_entry_put_deferred(entry);
return 0;
}
@ -4416,7 +4432,8 @@ kgsl_gpumem_vm_close(struct vm_area_struct *vma)
if (!atomic_dec_return(&entry->map_count))
atomic64_sub(entry->memdesc.size, &entry->priv->gpumem_mapped);
kgsl_mem_entry_put(entry);
entry->memdesc.useraddr = 0;
kgsl_mem_entry_put_deferred(entry);
}
static const struct vm_operations_struct kgsl_gpumem_vm_ops = {
@ -5137,6 +5154,12 @@ void kgsl_device_platform_remove(struct kgsl_device *device)
}
EXPORT_SYMBOL(kgsl_device_platform_remove);
static void
_flush_mem_workqueue(struct work_struct *work)
{
flush_workqueue(kgsl_driver.mem_workqueue);
}
static void kgsl_core_exit(void)
{
kgsl_events_exit();
@ -5252,6 +5275,8 @@ static int __init kgsl_core_init(void)
kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
INIT_WORK(&kgsl_driver.mem_work, _flush_mem_workqueue);
if (IS_ERR_VALUE(kgsl_run_one_worker(&kgsl_driver.worker,
&kgsl_driver.worker_thread,
"kgsl_worker_thread", true)) ||

View File

@ -124,6 +124,7 @@ struct kgsl_context;
* @full_cache_threshold: the threshold that triggers a full cache flush
* @workqueue: Pointer to a single threaded workqueue
* @mem_workqueue: Pointer to a workqueue for deferring memory entries
* @mem_work: Work struct to schedule mem_workqueue flush
*/
struct kgsl_driver {
struct cdev cdev;
@ -155,6 +156,7 @@ struct kgsl_driver {
unsigned int full_cache_threshold;
struct workqueue_struct *workqueue;
struct workqueue_struct *mem_workqueue;
struct work_struct mem_work;
struct kthread_worker worker;
struct kthread_worker low_prio_worker;
struct task_struct *worker_thread;
@ -459,6 +461,7 @@ long kgsl_ioctl_gpu_sparse_command(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data);
void kgsl_mem_entry_destroy(struct kref *kref);
void kgsl_mem_entry_destroy_deferred(struct kref *kref);
void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
int *egl_surface_count, int *egl_image_count);
@ -566,6 +569,21 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry)
kref_put(&entry->refcount, kgsl_mem_entry_destroy);
}
/**
* kgsl_mem_entry_put_deferred - Puts refcount and triggers deferred
* mem_entry destroy when refcount goes to zero.
* @entry: memory entry to be put.
*
* Use this to put a memory entry when we don't want to block
* the caller while destroying memory entry.
*/
static inline void
kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry)
{
if (entry)
kref_put(&entry->refcount, kgsl_mem_entry_destroy_deferred);
}
/*
* kgsl_addr_range_overlap() - Checks if 2 ranges overlap
* @gpuaddr1: Start of first address range

View File

@ -2555,13 +2555,37 @@ out:
return ret;
}
static int get_gpuaddr(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc, u64 start, u64 end,
u64 size, unsigned int align)
{
u64 addr;
int ret;
spin_lock(&pagetable->lock);
addr = _get_unmapped_area(pagetable, start, end, size, align);
if (addr == (u64) -ENOMEM) {
spin_unlock(&pagetable->lock);
return -ENOMEM;
}
ret = _insert_gpuaddr(pagetable, addr, size);
spin_unlock(&pagetable->lock);
if (ret == 0) {
memdesc->gpuaddr = addr;
memdesc->pagetable = pagetable;
}
return ret;
}
static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc)
{
struct kgsl_iommu_pt *pt = pagetable->priv;
int ret = 0;
uint64_t addr, start, end, size;
u64 start, end, size;
unsigned int align;
if (WARN_ON(kgsl_memdesc_use_cpu_map(memdesc)))
@ -2591,13 +2615,11 @@ static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable,
if (kgsl_memdesc_is_secured(memdesc))
start += secure_global_size;
spin_lock(&pagetable->lock);
addr = _get_unmapped_area(pagetable, start, end, size, align);
if (addr == (uint64_t) -ENOMEM) {
ret = -ENOMEM;
goto out;
ret = get_gpuaddr(pagetable, memdesc, start, end, size, align);
/* if OoM, retry once after flushing mem_wq */
if (ret == -ENOMEM) {
flush_workqueue(kgsl_driver.mem_workqueue);
ret = get_gpuaddr(pagetable, memdesc, start, end, size, align);
}
/*

View File

@ -498,6 +498,9 @@ static unsigned long
kgsl_pool_shrink_count_objects(struct shrinker *shrinker,
struct shrink_control *sc)
{
/* Trigger mem_workqueue flush to free memory */
kgsl_schedule_work(&kgsl_driver.mem_work);
/* Return total pool size as everything in pool can be freed */
return kgsl_pool_size_total();
}

View File

@ -860,6 +860,7 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
unsigned int pcount = 0;
size_t len;
unsigned int align;
bool memwq_flush_done = false;
static DEFINE_RATELIMIT_STATE(_rs,
DEFAULT_RATELIMIT_INTERVAL,
@ -936,6 +937,13 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
if (page_count == -EAGAIN)
continue;
/* if OoM, retry once after flushing mem_wq */
if (page_count == -ENOMEM && !memwq_flush_done) {
flush_workqueue(kgsl_driver.mem_workqueue);
memwq_flush_done = true;
continue;
}
/*
* Update sglen and memdesc size,as requested allocation
* not served fully. So that they can be correctly freed