• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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