xref: /linux/drivers/gpu/drm/amd/amdkfd/kfd_debugfs.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /*
3  * Copyright 2016-2022 Advanced Micro Devices, Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <linux/debugfs.h>
25 #include <linux/uaccess.h>
26 
27 #include "kfd_priv.h"
28 
29 static struct dentry *debugfs_root;
30 static struct dentry *debugfs_proc;
31 static struct list_head procs;
32 
33 struct debugfs_proc_entry {
34 	struct list_head list;
35 	struct dentry *proc_dentry;
36 	struct kfd_process *process;
37 	pid_t pid;
38 };
39 
40 #define MAX_DEBUGFS_FILENAME_LEN 32
41 
42 static int kfd_debugfs_open(struct inode *inode, struct file *file)
43 {
44 	int (*show)(struct seq_file *, void *) = inode->i_private;
45 
46 	return single_open(file, show, NULL);
47 }
48 static int kfd_debugfs_hang_hws_read(struct seq_file *m, void *data)
49 {
50 	seq_puts(m, "echo gpu_id > hang_hws\n");
51 	return 0;
52 }
53 
54 static ssize_t kfd_debugfs_hang_hws_write(struct file *file,
55 	const char __user *user_buf, size_t size, loff_t *ppos)
56 {
57 	struct kfd_node *dev;
58 	char tmp[16];
59 	uint32_t gpu_id;
60 	int ret = -EINVAL;
61 
62 	memset(tmp, 0, 16);
63 	if (size >= 16) {
64 		pr_err("Invalid input for gpu id.\n");
65 		goto out;
66 	}
67 	if (copy_from_user(tmp, user_buf, size)) {
68 		ret = -EFAULT;
69 		goto out;
70 	}
71 	if (kstrtoint(tmp, 10, &gpu_id)) {
72 		pr_err("Invalid input for gpu id.\n");
73 		goto out;
74 	}
75 	dev = kfd_device_by_id(gpu_id);
76 	if (dev) {
77 		kfd_debugfs_hang_hws(dev);
78 		ret = size;
79 	} else
80 		pr_err("Cannot find device %d.\n", gpu_id);
81 
82 out:
83 	return ret;
84 }
85 
86 static const struct file_operations kfd_debugfs_fops = {
87 	.owner = THIS_MODULE,
88 	.open = kfd_debugfs_open,
89 	.read = seq_read,
90 	.llseek = seq_lseek,
91 	.release = single_release,
92 };
93 
94 static const struct file_operations kfd_debugfs_hang_hws_fops = {
95 	.owner = THIS_MODULE,
96 	.open = kfd_debugfs_open,
97 	.read = seq_read,
98 	.write = kfd_debugfs_hang_hws_write,
99 	.llseek = seq_lseek,
100 	.release = single_release,
101 };
102 
103 void kfd_debugfs_init(void)
104 {
105 	debugfs_root = debugfs_create_dir("kfd", NULL);
106 	debugfs_proc = debugfs_create_dir("proc", debugfs_root);
107 	INIT_LIST_HEAD(&procs);
108 
109 	debugfs_create_file("mqds", S_IFREG | 0444, debugfs_root,
110 			    kfd_debugfs_mqds_by_process, &kfd_debugfs_fops);
111 	debugfs_create_file("hqds", S_IFREG | 0444, debugfs_root,
112 			    kfd_debugfs_hqds_by_device, &kfd_debugfs_fops);
113 	debugfs_create_file("rls", S_IFREG | 0444, debugfs_root,
114 			    kfd_debugfs_rls_by_device, &kfd_debugfs_fops);
115 	debugfs_create_file("hang_hws", S_IFREG | 0200, debugfs_root,
116 			    kfd_debugfs_hang_hws_read, &kfd_debugfs_hang_hws_fops);
117 	debugfs_create_file("mem_limit", S_IFREG | 0200, debugfs_root,
118 			    kfd_debugfs_kfd_mem_limits, &kfd_debugfs_fops);
119 }
120 
121 void kfd_debugfs_fini(void)
122 {
123 	debugfs_remove_recursive(debugfs_proc);
124 	debugfs_remove_recursive(debugfs_root);
125 }
126 
127 static ssize_t kfd_debugfs_pasid_read(struct file *file, char __user *buf,
128 				      size_t count, loff_t *ppos)
129 {
130 	struct kfd_process_device *pdd = file_inode(file)->i_private;
131 	char tmp[32];
132 	int len;
133 
134 	len = snprintf(tmp, sizeof(tmp), "%u\n", pdd->pasid);
135 
136 	return simple_read_from_buffer(buf, count, ppos, tmp, len);
137 }
138 
139 static const struct file_operations kfd_debugfs_pasid_fops = {
140 	.owner = THIS_MODULE,
141 	.read = kfd_debugfs_pasid_read,
142 };
143 
144 /* This helper locates the debugfs entry of a kfd process */
145 static struct debugfs_proc_entry *kfd_debugfs_find_process_entry(struct kfd_process *p)
146 {
147 	struct debugfs_proc_entry *entry;
148 
149 	list_for_each_entry(entry, &procs, list) {
150 		if (entry->process == p)
151 			return entry;
152 	}
153 
154 	return NULL;
155 }
156 
157 /* This helper creates pasid file of a kfd process under debugfs */
158 static void kfd_debugfs_create_pasid_files(struct kfd_process *p,
159 					   struct dentry *dir)
160 {
161 	char name[MAX_DEBUGFS_FILENAME_LEN];
162 	struct kfd_process_device *pdd;
163 	int i;
164 
165 	/* create pasid file for each GPU */
166 	for (i = 0; i < p->n_pdds; i++) {
167 		pdd = p->pdds[i];
168 		snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "pasid_%u", pdd->dev->id);
169 		debugfs_create_file((const char *)name, S_IFREG | 0444,
170 				    dir, pdd, &kfd_debugfs_pasid_fops);
171 	}
172 }
173 
174 int kfd_debugfs_add_process(struct kfd_process *p)
175 {
176 	struct debugfs_proc_entry *primary_entry;
177 	char name[MAX_DEBUGFS_FILENAME_LEN];
178 	struct kfd_process *primary_process;
179 	struct debugfs_proc_entry *entry;
180 	int ret;
181 
182 	entry = kzalloc_obj(*entry);
183 	if (!entry)
184 		return -ENOMEM;
185 
186 	entry->process = p;
187 	entry->pid = p->lead_thread->pid;
188 
189 	if (p->context_id == KFD_CONTEXT_ID_PRIMARY) {
190 		snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "%d",
191 			 (int)entry->pid);
192 		entry->proc_dentry = debugfs_create_dir(name, debugfs_proc);
193 	} else {
194 		primary_process = kfd_lookup_process_by_mm(p->lead_thread->mm);
195 		if (!primary_process) {
196 			ret = -ESRCH;
197 			goto err_free_entry;
198 		}
199 
200 		primary_entry = kfd_debugfs_find_process_entry(primary_process);
201 		kfd_unref_process(primary_process);
202 		if (!primary_entry) {
203 			pr_warn("Failed to find the primary debugfs entry for pid %d\n",
204 				entry->pid);
205 			ret = -ENOENT;
206 			goto err_free_entry;
207 		}
208 
209 		snprintf(name, MAX_DEBUGFS_FILENAME_LEN, "context_%u",
210 			 p->context_id);
211 		entry->proc_dentry = debugfs_create_dir(name,
212 							primary_entry->proc_dentry);
213 	}
214 
215 	list_add(&entry->list, &procs);
216 	kfd_debugfs_create_pasid_files(p, entry->proc_dentry);
217 
218 	return 0;
219 
220 err_free_entry:
221 	kfree(entry);
222 	return ret;
223 }
224 
225 /* This helper removes a debugfs entry and its sub-entries */
226 static void kfd_debugfs_remove_entry(struct debugfs_proc_entry *entry)
227 {
228 	debugfs_remove(entry->proc_dentry);
229 	list_del(&entry->list);
230 	kfree(entry);
231 }
232 
233 void kfd_debugfs_remove_process(struct kfd_process *p)
234 {
235 	struct debugfs_proc_entry *entry, *next;
236 
237 	mutex_lock(&kfd_processes_mutex);
238 	if (p->context_id == KFD_CONTEXT_ID_PRIMARY) {
239 		/* remove entries of secondary contexts */
240 		list_for_each_entry_safe(entry, next, &procs, list) {
241 			if (entry->pid != p->lead_thread->pid || entry->process == p)
242 				continue;
243 
244 			kfd_debugfs_remove_entry(entry);
245 		}
246 	}
247 
248 	list_for_each_entry_safe(entry, next, &procs, list) {
249 		if (entry->process != p)
250 			continue;
251 
252 		kfd_debugfs_remove_entry(entry);
253 	}
254 
255 	mutex_unlock(&kfd_processes_mutex);
256 }
257