diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index e4dc53a364282457e6a85fbc9a09f27ad561c401..fe7b0f2959c218f16fb7f017cd62bf24ca26c51b 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -30,6 +30,19 @@ config SW_SYNC WARNING: improper use of this can result in deadlocking kernel drivers from userspace. Intended for test and debug only. +config DMABUF_PROCESS_INFO + bool "Show dmabuf usage of all processes" + default n + depends on DMA_SHARED_BUFFER + depends on PROC_FS || DEBUG_FS + help + Choose this option to show dmabuf objects usage of all processes. + Firstly, with this option, when a process creates a dmabuf object, + its pid and task_comm will be recorded in the dmabuf. + Secondly, this option creates dma_buf/process_bufinfo file in + debugfs (if DEBUG_FS enabled) and process_dmabuf_info file in procfs + (if PROC_FS enabled) to show dmabuf objects usage of all processes. + config UDMABUF bool "userspace dmabuf misc driver" default n diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 70ec901edf2c59f427e9b09e159426eb65405377..f5309adfb83a6f77e5efa0b370ef2f8e0b0b3e4d 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -16,3 +16,4 @@ dmabuf_selftests-y := \ st-dma-resv.o obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o +obj-$(CONFIG_DMABUF_PROCESS_INFO) += dma-buf-process-info.o diff --git a/drivers/dma-buf/dma-buf-process-info.c b/drivers/dma-buf/dma-buf-process-info.c new file mode 100755 index 0000000000000000000000000000000000000000..4b7699092bf29b5185e903f9a7751e137cc3bb63 --- /dev/null +++ b/drivers/dma-buf/dma-buf-process-info.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMA-BUF: dmabuf usage of all processes statistics. + * + * Copyright (c) 2024 Huawei Device Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "dma-buf-process-info.h" + +static struct proc_dir_entry *proc_dmabuf_entry; + +struct dmabuf_task_info_args { + struct seq_file *seq; + struct task_struct *tsk; + size_t tsk_dmabuf_bytes; +}; + +void init_dma_buf_task_info(struct dma_buf *buf) +{ + struct task_struct *tsk = NULL; + + if (IS_ERR_OR_NULL(buf)) + return; + + get_task_struct(current->group_leader); + task_lock(current->group_leader); + tsk = current->group_leader; + buf->exp_pid = task_pid_nr(tsk); + if (tsk->flags & PF_KTHREAD) + tsk = NULL; + task_unlock(current->group_leader); + put_task_struct(current->group_leader); + + if (tsk) + get_task_comm(buf->exp_task_comm, tsk); + else /* kernel task */ + strncpy(buf->exp_task_comm, "[kernel task]", + sizeof(buf->exp_task_comm)); +} + +pid_t dma_buf_exp_pid(const struct dma_buf *buf) +{ + if (IS_ERR_OR_NULL(buf)) + return 0; + + return buf->exp_pid; +} + +const char *dma_buf_exp_task_comm(const struct dma_buf *buf) +{ + if (IS_ERR_OR_NULL(buf)) + return NULL; + + return buf->exp_task_comm; +} + +static int dma_buf_single_file_show(const void *data, struct file *f, + unsigned int fd) +{ + struct dmabuf_task_info_args *tsk_info = NULL; + struct task_struct *tsk = NULL; + struct dma_buf *buf = NULL; + + tsk_info = (struct dmabuf_task_info_args *)data; + if (IS_ERR_OR_NULL(tsk_info) || IS_ERR_OR_NULL(tsk_info->seq)) + return 0; + + tsk = tsk_info->tsk; + buf = get_dma_buf_from_file(f); + if (IS_ERR_OR_NULL(tsk) || IS_ERR_OR_NULL(buf)) + return 0; + + tsk_info->tsk_dmabuf_bytes += buf->size; + + spin_lock(&buf->name_lock); + seq_printf(tsk_info->seq, + "%-16s %-16d %-16u %-16zu %-16lu %-16d %-16s %s \t %s\n", + tsk->comm, + tsk->pid, + fd, + buf->size, + file_inode(buf->file)->i_ino, + buf->exp_pid, + buf->exp_task_comm, + buf->name ?: "NULL", + buf->exp_name ?: "NULL"); + spin_unlock(&buf->name_lock); + + return 0; +} + +static int dma_buf_process_info_show(struct seq_file *s, void *unused) +{ + struct dmabuf_task_info_args task_info = { NULL, NULL, 0 }; + struct task_struct *tsk = NULL; + + seq_puts(s, "Dma-buf objects usage of processes:\n"); + seq_printf(s, "%-16s %-16s %-16s %-16s %-16s %-16s %-16s %s \t %s\n", + "Process", "pid", "fd", "size_bytes", "ino", "exp_pid", + "exp_task_comm", "buf_name", "exp_name"); + + task_info.seq = s; + + rcu_read_lock(); + for_each_process(tsk) { + task_info.tsk = tsk; + task_info.tsk_dmabuf_bytes = 0; + + task_lock(tsk); + iterate_fd(tsk->files, 0, dma_buf_single_file_show, + (void *)&task_info); + if (task_info.tsk_dmabuf_bytes) + seq_printf(s, "Total dmabuf size of %s: %zu bytes\n", + tsk->comm, task_info.tsk_dmabuf_bytes); + task_unlock(tsk); + } + rcu_read_unlock(); + + return 0; +} + +void dma_buf_process_info_init_procfs(void) +{ + proc_dmabuf_entry = proc_create_single("process_dmabuf_info", 0444, + NULL, + dma_buf_process_info_show); + if (!proc_dmabuf_entry) + pr_err("%s: create node /proc/process_dmabuf_info failed\n", + __func__); +} + +void dma_buf_process_info_uninit_procfs(void) +{ + if (!proc_dmabuf_entry) + return; + + proc_remove(proc_dmabuf_entry); +} + +DEFINE_SHOW_ATTRIBUTE(dma_buf_process_info); + +int dma_buf_process_info_init_debugfs(struct dentry *parent) +{ + struct dentry *debugfs_file = NULL; + int err = 0; + + if (IS_ERR_OR_NULL(parent)) + return -EINVAL; + + debugfs_file = debugfs_create_file("process_bufinfo", 0444, + parent, NULL, + &dma_buf_process_info_fops); + if (IS_ERR(debugfs_file)) { + pr_err("dma_buf: debugfs: create process_bufinfo failed\n"); + err = PTR_ERR(debugfs_file); + } + + return err; +} \ No newline at end of file diff --git a/drivers/dma-buf/dma-buf-process-info.h b/drivers/dma-buf/dma-buf-process-info.h new file mode 100755 index 0000000000000000000000000000000000000000..50e84a179af395b27c1a0b0c6b434e758db5b67f --- /dev/null +++ b/drivers/dma-buf/dma-buf-process-info.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMA-BUF: dmabuf usage of all processes statistics. + * + * Copyright (c) 2024 Huawei Device Co., Ltd. + */ + +#ifndef __DMA_BUF_PROCESS_INFO_H +#define __DMA_BUF_PROCESS_INFO_H + +#ifdef CONFIG_DMABUF_PROCESS_INFO +/** + * init_dma_buf_task_info - init exp_pid and exp_task_comm of dma_buf + * @buf: [in] pointer to struct dma_buf. If @buf IS_ERR_OR_NULL, + * return with doing nothing. + */ +void init_dma_buf_task_info(struct dma_buf *buf); + +/** + * dma_buf_exp_pid - return exp_pid of @buf + * @buf: [in] pointer to struct dma_buf + * + * Return 0 if @buf IS_ERR_OR_NULL, else return buf->exp_pid + */ +pid_t dma_buf_exp_pid(const struct dma_buf *buf); + +/** + * dma_buf_exp_task_comm - return exp_task_comm of @buf + * @buf: [in] pointer to struct dma_buf + * + * Return NULL if @buf IS_ERR_OR_NULL, else return buf->exp_task_comm + */ +const char *dma_buf_exp_task_comm(const struct dma_buf *buf); + +/** + * dma_buf_process_info_init_procfs - module init: create node in procfs + */ +void dma_buf_process_info_init_procfs(void); + +/** + * dma_buf_process_info_uninit_procfs - module exit: remove node in procfs + */ +void dma_buf_process_info_uninit_procfs(void); + +/** + * dma_buf_process_info_init_debugfs - create debug node under @parent + * in debugfs. + * @parent: [in] pointer to struct dentry. If @parent IS_ERR_OR_NULL, + * return -EINVAL + * + * Return 0 if success, otherwise return errno. + * + * Note that there is no related uninit function, since the debug node will + * be removed in dma_buf_uninit_debugfs() when dma_buf_deinit() called. + */ +int dma_buf_process_info_init_debugfs(struct dentry *parent); + +#else /* CONFIG_DMABUF_PROCESS_INFO */ + +static inline void init_dma_buf_task_info(struct dma_buf *buf) {} + +static inline pid_t dma_buf_exp_pid(const struct dma_buf *buf) +{ + return 0; +} + +static inline const char *dma_buf_exp_task_comm(const struct dma_buf *buf) +{ + return NULL; +} + +static inline void dma_buf_process_info_init_procfs(void) {} + +static inline void dma_buf_process_info_uninit_procfs(void) {} + +static inline int +dma_buf_process_info_init_debugfs(struct dentry *parent) +{ + return 0; +} +#endif /* CONFIG_DMABUF_PROCESS_INFO */ +#endif /* __DMA_BUF_PROCESS_INFO_H */ \ No newline at end of file diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 21916bba77d58be342d2d82dc654c7df330f01fb..e5bd2e015c75df18672eae9a8f1856820e3ca1f0 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -32,6 +32,7 @@ #include #include "dma-buf-sysfs-stats.h" +#include "dma-buf-process-info.h" static inline int is_dma_buf_file(struct file *); @@ -676,6 +677,7 @@ 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); + init_dma_buf_task_info(dmabuf); return dmabuf; err_dmabuf: @@ -1617,8 +1619,10 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) return ret; seq_puts(s, "\nDma-buf Objects:\n"); - seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\tname\n", - "size", "flags", "mode", "count", "ino"); + seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\t" + "%-16s\t%-16s\t%-16s\n", + "size", "flags", "mode", "count", "ino", + "buf_name", "exp_pid", "exp_task_comm"); list_for_each_entry(buf_obj, &db_list.head, list_node) { @@ -1628,13 +1632,16 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) spin_lock(&buf_obj->name_lock); - seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\n", + seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\t" + "%-16d\t%-16s\n", buf_obj->size, buf_obj->file->f_flags, buf_obj->file->f_mode, file_count(buf_obj->file), buf_obj->exp_name, file_inode(buf_obj->file)->i_ino, - buf_obj->name ?: ""); + buf_obj->name ?: "NULL", + dma_buf_exp_pid(buf_obj), + dma_buf_exp_task_comm(buf_obj) ?: "NULL"); spin_unlock(&buf_obj->name_lock); dma_resv_describe(buf_obj->resv, s); @@ -1689,6 +1696,7 @@ static int dma_buf_init_debugfs(void) err = PTR_ERR(d); } + dma_buf_process_info_init_debugfs(dma_buf_debugfs_dir); return err; } @@ -1706,6 +1714,19 @@ static inline void dma_buf_uninit_debugfs(void) } #endif +#ifdef CONFIG_DMABUF_PROCESS_INFO +struct dma_buf *get_dma_buf_from_file(struct file *f) +{ + if (IS_ERR_OR_NULL(f)) + return NULL; + + if (!is_dma_buf_file(f)) + return NULL; + + return f->private_data; +} +#endif /* CONFIG_DMABUF_PROCESS_INFO */ + static int __init dma_buf_init(void) { int ret; @@ -1721,6 +1742,7 @@ static int __init dma_buf_init(void) mutex_init(&db_list.lock); INIT_LIST_HEAD(&db_list.head); dma_buf_init_debugfs(); + dma_buf_process_info_init_procfs(); return 0; } subsys_initcall(dma_buf_init); @@ -1730,5 +1752,6 @@ static void __exit dma_buf_deinit(void) dma_buf_uninit_debugfs(); kern_unmount(dma_buf_mnt); dma_buf_uninit_sysfs_statistics(); + dma_buf_process_info_uninit_procfs(); } __exitcall(dma_buf_deinit); diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 3f31baa3293f9819e271911db351456ad2a14519..829befa7405543bd80c8c2fab73c35f6d836126b 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -370,6 +370,11 @@ struct dma_buf { /** @list_node: node for dma_buf accounting and debugging. */ struct list_head list_node; +#ifdef CONFIG_DMABUF_PROCESS_INFO + pid_t exp_pid; + char exp_task_comm[TASK_COMM_LEN]; +#endif + /** @priv: exporter specific private data for this buffer object. */ void *priv; @@ -631,4 +636,17 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map); void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map); int dma_buf_vmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); void dma_buf_vunmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); + +#ifdef CONFIG_DMABUF_PROCESS_INFO +/** + * get_dma_buf_from_file - Get struct dma_buf* from struct file* + * @f: [in] pointer to struct file, which is associated with a + * dma_buf object. + * + * If @f IS_ERR_OR_NULL, return NULL. + * If @f is not a file associated with dma_buf, return NULL. + */ +struct dma_buf *get_dma_buf_from_file(struct file *f); +#endif /* CONFIG_DMABUF_PROCESS_INFO */ + #endif /* __DMA_BUF_H__ */