1// SPDX-License-Identifier: GPL-2.0
2/*
3 * DMA-BUF: dmabuf usage of all processes statistics.
4 *
5 * Copyright (c) 2022 Huawei Device Co., Ltd.
6 */
7
8#include <linux/debugfs.h>
9#include <linux/dma-buf.h>
10#include <linux/fdtable.h>
11#include <linux/proc_fs.h>
12#include <linux/seq_file.h>
13
14#include "dma-buf-process-info.h"
15
16static struct proc_dir_entry *proc_dmabuf_entry;
17
18struct dmabuf_task_info_args {
19	struct seq_file *seq;
20	struct task_struct *tsk;
21	size_t tsk_dmabuf_bytes;
22};
23
24void init_dma_buf_task_info(struct dma_buf *buf)
25{
26	struct task_struct *tsk = NULL;
27
28	if (IS_ERR_OR_NULL(buf))
29		return;
30
31	get_task_struct(current->group_leader);
32	task_lock(current->group_leader);
33	tsk = current->group_leader;
34	buf->exp_pid = task_pid_nr(tsk);
35	if (tsk->flags & PF_KTHREAD)
36		tsk = NULL;
37	task_unlock(current->group_leader);
38	put_task_struct(current->group_leader);
39
40	if (tsk)
41		get_task_comm(buf->exp_task_comm, tsk);
42	else /* kernel task */
43		strncpy(buf->exp_task_comm, "[kernel task]",
44			sizeof(buf->exp_task_comm));
45}
46
47pid_t dma_buf_exp_pid(const struct dma_buf *buf)
48{
49	if (IS_ERR_OR_NULL(buf))
50		return 0;
51
52	return buf->exp_pid;
53}
54
55const char *dma_buf_exp_task_comm(const struct dma_buf *buf)
56{
57	if (IS_ERR_OR_NULL(buf))
58		return NULL;
59
60	return buf->exp_task_comm;
61}
62
63static int dma_buf_single_file_show(const void *data, struct file *f,
64				    unsigned int fd)
65{
66	struct dmabuf_task_info_args *tsk_info = NULL;
67	struct task_struct *tsk = NULL;
68	struct dma_buf *buf = NULL;
69
70	tsk_info = (struct dmabuf_task_info_args *)data;
71	if (IS_ERR_OR_NULL(tsk_info) || IS_ERR_OR_NULL(tsk_info->seq))
72		return 0;
73
74	tsk = tsk_info->tsk;
75	buf = get_dma_buf_from_file(f);
76	if (IS_ERR_OR_NULL(tsk) || IS_ERR_OR_NULL(buf))
77		return 0;
78
79	tsk_info->tsk_dmabuf_bytes += buf->size;
80
81	spin_lock(&buf->name_lock);
82	seq_printf(tsk_info->seq,
83		   "%-16s %-16d %-16u %-16zu %-16lu %-16d %-16s %s \t %s\n",
84		   tsk->comm,
85		   tsk->pid,
86		   fd,
87		   buf->size,
88		   file_inode(buf->file)->i_ino,
89		   buf->exp_pid,
90		   buf->exp_task_comm,
91		   buf->name ?: "NULL",
92		   buf->exp_name ?: "NULL");
93	spin_unlock(&buf->name_lock);
94
95	return 0;
96}
97
98static int dma_buf_process_info_show(struct seq_file *s, void *unused)
99{
100	struct dmabuf_task_info_args task_info = { NULL, NULL, 0 };
101	struct task_struct *tsk = NULL;
102
103	seq_puts(s, "Dma-buf objects usage of processes:\n");
104	seq_printf(s, "%-16s %-16s %-16s %-16s %-16s %-16s %-16s %s \t %s\n",
105		   "Process", "pid", "fd", "size_bytes", "ino", "exp_pid",
106		   "exp_task_comm", "buf_name", "exp_name");
107
108	task_info.seq = s;
109
110	rcu_read_lock();
111	for_each_process(tsk) {
112		task_info.tsk = tsk;
113		task_info.tsk_dmabuf_bytes = 0;
114
115		task_lock(tsk);
116		iterate_fd(tsk->files, 0, dma_buf_single_file_show,
117			   (void *)&task_info);
118		if (task_info.tsk_dmabuf_bytes)
119			seq_printf(s, "Total dmabuf size of %s: %zu bytes\n",
120				   tsk->comm, task_info.tsk_dmabuf_bytes);
121		task_unlock(tsk);
122	}
123	rcu_read_unlock();
124
125	return 0;
126}
127
128void dma_buf_process_info_init_procfs(void)
129{
130	proc_dmabuf_entry = proc_create_single("process_dmabuf_info", 0444,
131					       NULL,
132					       dma_buf_process_info_show);
133	if (!proc_dmabuf_entry)
134		pr_err("%s: create node /proc/process_dmabuf_info failed\n",
135		       __func__);
136}
137
138void dma_buf_process_info_uninit_procfs(void)
139{
140	if (!proc_dmabuf_entry)
141		return;
142
143	proc_remove(proc_dmabuf_entry);
144}
145
146DEFINE_SHOW_ATTRIBUTE(dma_buf_process_info);
147
148int dma_buf_process_info_init_debugfs(struct dentry *parent)
149{
150	struct dentry *debugfs_file = NULL;
151	int err = 0;
152
153	if (IS_ERR_OR_NULL(parent))
154		return -EINVAL;
155
156	debugfs_file = debugfs_create_file("process_bufinfo", 0444,
157					   parent, NULL,
158					   &dma_buf_process_info_fops);
159	if (IS_ERR(debugfs_file)) {
160		pr_err("dma_buf: debugfs: create process_bufinfo failed\n");
161		err = PTR_ERR(debugfs_file);
162	}
163
164	return err;
165}
166