diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 907cbbfd4bff..3f6e06306c5b 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 9967667d9c48..85d67d0dc2bb 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -561,6 +561,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);