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
16 static struct proc_dir_entry *proc_dmabuf_entry;
17
18 struct dmabuf_task_info_args {
19 struct seq_file *seq;
20 struct task_struct *tsk;
21 size_t tsk_dmabuf_bytes;
22 };
23
init_dma_buf_task_info(struct dma_buf * buf)24 void 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
dma_buf_exp_pid(const struct dma_buf * buf)47 pid_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
dma_buf_exp_task_comm(const struct dma_buf * buf)55 const 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
dma_buf_single_file_show(const void * data,struct file * f,unsigned int fd)63 static 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
dma_buf_process_info_show(struct seq_file * s,void * unused)98 static 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
dma_buf_process_info_init_procfs(void)128 void 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
dma_buf_process_info_uninit_procfs(void)138 void dma_buf_process_info_uninit_procfs(void)
139 {
140 if (!proc_dmabuf_entry)
141 return;
142
143 proc_remove(proc_dmabuf_entry);
144 }
145
146 DEFINE_SHOW_ATTRIBUTE(dma_buf_process_info);
147
dma_buf_process_info_init_debugfs(struct dentry * parent)148 int 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