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