BACKPORT: FROMLIST: dma-buf: Move sysfs work out of DMA-BUF export path
We have identified an animation lag issue on our Android 14-6.1 product which seems to be caused by contention in the rwsem lock during the dmabuf request process. It appears that other processes are holding sysfs read locks, resulting in the blocking of dmabuf sysfs node creation. We encountered an issue in android14-6.1 that is similar to the problem described in [1]. So we cherry-pick this commit to android14-6.1. [1] https://android-review.googlesource.com/c/kernel/common/+/2111974 Bug: 311282169 Bug: 206979019 Link: https://lore.kernel.org/lkml/CABdmKX2dNYhgOYdrrJU6-jt6F=LjCidbKhR6t4F7yaa0SPr+-A@mail.gmail.com/T/ Signed-off-by: Dezhi Huang <huangdezhi@hihonor.com> Conflicts: include/linux/dma-buf.h 1. The android14-6.1 KMI is frozen, and the modification to struct dma_buf_sysfs_entry in the original patch triggers ABI check failures. Instead of an anonymous union, use the existing struct kobject directly as a work_struct with type punning. Signed-off-by: T.J. Mercier <tjmercier@google.com> Change-Id: Ic0386849b6b248b0a72215633fc1a50782455bac
This commit is contained in:
parent
928b3b5dde
commit
c1b1201d39
@ -11,6 +11,7 @@
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "dma-buf-sysfs-stats.h"
|
||||
|
||||
@ -168,35 +169,72 @@ void dma_buf_uninit_sysfs_statistics(void)
|
||||
kset_unregister(dma_buf_stats_kset);
|
||||
}
|
||||
|
||||
static void sysfs_add_workfn(struct work_struct *work)
|
||||
{
|
||||
/* The ABI would have to change for this to be false, but let's be paranoid. */
|
||||
_Static_assert(sizeof(struct kobject) >= sizeof(struct work_struct),
|
||||
"kobject is smaller than work_struct");
|
||||
|
||||
struct dma_buf_sysfs_entry *sysfs_entry =
|
||||
container_of((struct kobject *)work, struct dma_buf_sysfs_entry, kobj);
|
||||
struct dma_buf *dmabuf = sysfs_entry->dmabuf;
|
||||
|
||||
/*
|
||||
* A dmabuf is ref-counted via its file member. If this handler holds the only
|
||||
* reference to the dmabuf, there is no need for sysfs kobject creation. This is an
|
||||
* optimization and a race; when the reference count drops to 1 immediately after
|
||||
* this check it is not harmful as the sysfs entry will still get cleaned up in
|
||||
* dma_buf_stats_teardown, which won't get called until the final dmabuf reference
|
||||
* is released, and that can't happen until the end of this function.
|
||||
*/
|
||||
if (file_count(dmabuf->file) > 1) {
|
||||
/*
|
||||
* kobject_init_and_add expects kobject to be zero-filled, but we have populated it
|
||||
* (the sysfs_add_work union member) to trigger this work function.
|
||||
*/
|
||||
memset(&dmabuf->sysfs_entry->kobj, 0, sizeof(dmabuf->sysfs_entry->kobj));
|
||||
dmabuf->sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset;
|
||||
if (kobject_init_and_add(&dmabuf->sysfs_entry->kobj, &dma_buf_ktype, NULL,
|
||||
"%lu", file_inode(dmabuf->file)->i_ino)) {
|
||||
kobject_put(&dmabuf->sysfs_entry->kobj);
|
||||
dmabuf->sysfs_entry = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Free the sysfs_entry and reset the pointer so dma_buf_stats_teardown doesn't
|
||||
* attempt to operate on it.
|
||||
*/
|
||||
kfree(dmabuf->sysfs_entry);
|
||||
dmabuf->sysfs_entry = NULL;
|
||||
}
|
||||
dma_buf_put(dmabuf);
|
||||
}
|
||||
|
||||
int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
|
||||
{
|
||||
struct dma_buf_sysfs_entry *sysfs_entry;
|
||||
int ret;
|
||||
struct work_struct *work;
|
||||
|
||||
if (!dmabuf->exp_name) {
|
||||
pr_err("exporter name must not be empty if stats needed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sysfs_entry = kzalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL);
|
||||
sysfs_entry = kmalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL);
|
||||
if (!sysfs_entry)
|
||||
return -ENOMEM;
|
||||
|
||||
sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset;
|
||||
sysfs_entry->dmabuf = dmabuf;
|
||||
|
||||
dmabuf->sysfs_entry = sysfs_entry;
|
||||
|
||||
/* create the directory for buffer stats */
|
||||
ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL,
|
||||
"%lu", file_inode(file)->i_ino);
|
||||
if (ret)
|
||||
goto err_sysfs_dmabuf;
|
||||
/*
|
||||
* The use of kobj as a work_struct is an ugly hack
|
||||
* to avoid an ABI break in this frozen kernel.
|
||||
*/
|
||||
work = (struct work_struct *)&dmabuf->sysfs_entry->kobj;
|
||||
INIT_WORK(work, sysfs_add_workfn);
|
||||
get_dma_buf(dmabuf); /* This reference will be dropped in sysfs_add_workfn. */
|
||||
schedule_work(work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs_dmabuf:
|
||||
kobject_put(&sysfs_entry->kobj);
|
||||
dmabuf->sysfs_entry = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
@ -727,10 +727,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
|
||||
dmabuf->resv = resv;
|
||||
}
|
||||
|
||||
ret = dma_buf_stats_setup(dmabuf, file);
|
||||
if (ret)
|
||||
goto err_dmabuf;
|
||||
|
||||
file->private_data = dmabuf;
|
||||
file->f_path.dentry->d_fsdata = dmabuf;
|
||||
dmabuf->file = file;
|
||||
@ -739,9 +735,19 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
|
||||
list_add(&dmabuf->list_node, &db_list.head);
|
||||
mutex_unlock(&db_list.lock);
|
||||
|
||||
ret = dma_buf_stats_setup(dmabuf, file);
|
||||
if (ret)
|
||||
goto err_sysfs;
|
||||
|
||||
return dmabuf;
|
||||
|
||||
err_dmabuf:
|
||||
err_sysfs:
|
||||
mutex_lock(&db_list.lock);
|
||||
list_del(&dmabuf->list_node);
|
||||
mutex_unlock(&db_list.lock);
|
||||
dmabuf->file = NULL;
|
||||
file->f_path.dentry->d_fsdata = NULL;
|
||||
file->private_data = NULL;
|
||||
if (!resv)
|
||||
dma_resv_fini(dmabuf->resv);
|
||||
kfree(dmabuf);
|
||||
|
Loading…
Reference in New Issue
Block a user