From 0b1bc8a213194d9e6d8b253acc5d6b0c56765fa6 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Tue, 10 Aug 2021 19:25:27 +0530 Subject: [PATCH] msm: kgsl: Use worker to put refcount on mem entry If refcount on mem entry is the last refcount, we will call kgsl_mem_entry_destroy and detach it from process list. If there is no refcount on the process private as well then we will call kgsl_destroy_process_private to cleanup. During cleanup, we will try to remove the same sysfs node which is in use by the current thread and this situation will end up in a deadlock. To fix this problem, use a worker to put the refcount on mem entry. Change-Id: I69a77c4059489c95de2ca205701571daac4bdc8a Signed-off-by: Pankaj Gupta --- drivers/gpu/msm/kgsl.c | 10 ++++++++-- drivers/gpu/msm/kgsl.h | 7 +++++++ drivers/gpu/msm/kgsl_sharedmem.c | 21 +++++++++++++-------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 565ddfca6d6e..ba8ef281962d 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -248,6 +248,13 @@ static void _deferred_put(struct work_struct *work) kgsl_mem_entry_put(entry); } +/* Use a worker to put the refcount on mem entry */ +void kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry) +{ + INIT_WORK(&entry->work, _deferred_put); + queue_work(kgsl_driver.mem_workqueue, &entry->work); +} + static struct kgsl_mem_entry *kgsl_mem_entry_create(void) { struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL); @@ -2336,8 +2343,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; } diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index bb5ee365117d..6b19e7d52032 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -566,6 +566,13 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry) kref_put(&entry->refcount, kgsl_mem_entry_destroy); } +/** + * kgsl_mem_entry_put_deferred() - use a worker to put the refcount + * on mem entry from a sysfs handler or debugfs handler. + * @entry - The memory entry + */ +void kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry); + /* * kgsl_addr_range_overlap() - Checks if 2 ranges overlap * @gpuaddr1: Start of first address range diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 599c5b61e9f8..481bbf8f2a68 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -136,7 +136,18 @@ imported_mem_show(struct kgsl_process_private *priv, } } - kgsl_mem_entry_put(entry); + /* + * If refcount on mem entry is the last refcount, we will + * call kgsl_mem_entry_destroy and detach it from process + * list. When there is no refcount on the process private, + * we will call kgsl_destroy_process_private to do cleanup. + * During cleanup, we will try to remove the same sysfs + * node which is in use by the current thread and this + * situation will end up in a deadloack. + * To avoid this situation, use a worker to put the refcount + * on mem entry. + */ + kgsl_mem_entry_put_deferred(entry); spin_lock(&priv->mem_lock); } spin_unlock(&priv->mem_lock); @@ -227,12 +238,6 @@ static ssize_t memtype_sysfs_show(struct kobject *kobj, u64 size = 0; int id = 0; - /* - * sysfs_remove_file waits for reads to complete before the node is - * deleted and process private is freed only once kobj is released. - * This implies that priv will not be freed until this function - * completes, and no further locking is needed. - */ priv = container_of(kobj, struct kgsl_process_private, kobj_memtype); memtype = container_of(attr, struct kgsl_memtype, attr); @@ -253,7 +258,7 @@ static ssize_t memtype_sysfs_show(struct kobject *kobj, if (type == memtype->type) size += memdesc->size; - kgsl_mem_entry_put(entry); + kgsl_mem_entry_put_deferred(entry); spin_lock(&priv->mem_lock); } spin_unlock(&priv->mem_lock);