xref: /linux/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c (revision c31f4aa8fed048fa70e742c4bb49bb48dc489ab3)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2025 Intel Corporation
4  */
5 
6 #include <linux/debugfs.h>
7 #include <drm/drm_debugfs.h>
8 
9 #include "xe_device.h"
10 #include "xe_device_types.h"
11 #include "xe_pm.h"
12 #include "xe_sriov_pf.h"
13 #include "xe_sriov_pf_control.h"
14 #include "xe_sriov_pf_debugfs.h"
15 #include "xe_sriov_pf_helpers.h"
16 #include "xe_sriov_pf_migration.h"
17 #include "xe_sriov_pf_provision.h"
18 #include "xe_sriov_pf_service.h"
19 #include "xe_sriov_printk.h"
20 #include "xe_tile_sriov_pf_debugfs.h"
21 
22 /*
23  *      /sys/kernel/debug/dri/BDF/
24  *      ├── sriov		# d_inode->i_private = (xe_device*)
25  *      │   ├── pf		# d_inode->i_private = (xe_device*)
26  *      │   ├── vf1		# d_inode->i_private = VFID(1)
27  *      :   :
28  *      │   ├── vfN		# d_inode->i_private = VFID(N)
29  */
30 
31 static void *extract_priv(struct dentry *d)
32 {
33 	return d->d_inode->i_private;
34 }
35 
36 static struct xe_device *extract_xe(struct dentry *d)
37 {
38 	return extract_priv(d->d_parent);
39 }
40 
41 static unsigned int extract_vfid(struct dentry *d)
42 {
43 	void *p = extract_priv(d);
44 
45 	return p == extract_xe(d) ? PFID : (uintptr_t)p;
46 }
47 
48 /*
49  *      /sys/kernel/debug/dri/BDF/
50  *      ├── sriov
51  *      │   ├── restore_auto_provisioning
52  *      │   :
53  *      │   ├── pf/
54  *      │   ├── vf1
55  *      │   │   ├── ...
56  */
57 
58 static ssize_t from_file_write_to_xe_call(struct file *file, const char __user *userbuf,
59 					  size_t count, loff_t *ppos,
60 					  int (*call)(struct xe_device *))
61 {
62 	struct dentry *dent = file_dentry(file);
63 	struct xe_device *xe = extract_xe(dent);
64 	bool yes;
65 	int ret;
66 
67 	if (*ppos)
68 		return -EINVAL;
69 	ret = kstrtobool_from_user(userbuf, count, &yes);
70 	if (ret < 0)
71 		return ret;
72 	if (yes) {
73 		xe_pm_runtime_get(xe);
74 		ret = call(xe);
75 		xe_pm_runtime_put(xe);
76 	}
77 	if (ret < 0)
78 		return ret;
79 	return count;
80 }
81 
82 #define DEFINE_SRIOV_ATTRIBUTE(OP)						\
83 static int OP##_show(struct seq_file *s, void *unused)				\
84 {										\
85 	return 0;								\
86 }										\
87 static ssize_t OP##_write(struct file *file, const char __user *userbuf,	\
88 			  size_t count, loff_t *ppos)				\
89 {										\
90 	return from_file_write_to_xe_call(file, userbuf, count, ppos,		\
91 					  xe_sriov_pf_##OP);			\
92 }										\
93 DEFINE_SHOW_STORE_ATTRIBUTE(OP)
94 
95 static inline int xe_sriov_pf_restore_auto_provisioning(struct xe_device *xe)
96 {
97 	return xe_sriov_pf_provision_set_mode(xe, XE_SRIOV_PROVISIONING_MODE_AUTO);
98 }
99 
100 DEFINE_SRIOV_ATTRIBUTE(restore_auto_provisioning);
101 
102 static int lockdown_vfs_enabling_open(struct inode *inode, struct file *file)
103 {
104 	struct dentry *dent = file_dentry(file);
105 	struct xe_device *xe = extract_xe(dent);
106 	ssize_t ret;
107 
108 	ret = xe_sriov_pf_lockdown(xe);
109 	if (ret < 0)
110 		return ret;
111 
112 	file->private_data = xe;
113 	return nonseekable_open(inode, file);
114 }
115 
116 static int lockdown_vfs_enabling_release(struct inode *inode, struct file *file)
117 {
118 	struct xe_device *xe = file->private_data;
119 
120 	xe_sriov_pf_end_lockdown(xe);
121 	return 0;
122 }
123 
124 static const struct file_operations lockdown_vfs_enabling_fops = {
125 	.owner		= THIS_MODULE,
126 	.open		= lockdown_vfs_enabling_open,
127 	.release	= lockdown_vfs_enabling_release,
128 };
129 
130 static void pf_populate_root(struct xe_device *xe, struct dentry *dent)
131 {
132 	debugfs_create_file("restore_auto_provisioning", 0200, dent, xe,
133 			    &restore_auto_provisioning_fops);
134 	debugfs_create_file("lockdown_vfs_enabling", 0400, dent, xe,
135 			    &lockdown_vfs_enabling_fops);
136 }
137 
138 static int simple_show(struct seq_file *m, void *data)
139 {
140 	struct drm_printer p = drm_seq_file_printer(m);
141 	struct drm_info_node *node = m->private;
142 	struct dentry *parent = node->dent->d_parent;
143 	struct xe_device *xe = parent->d_inode->i_private;
144 	void (*print)(struct xe_device *, struct drm_printer *) = node->info_ent->data;
145 
146 	print(xe, &p);
147 	return 0;
148 }
149 
150 static const struct drm_info_list debugfs_list[] = {
151 	{ .name = "vfs", .show = simple_show, .data = xe_sriov_pf_print_vfs_summary },
152 	{ .name = "versions", .show = simple_show, .data = xe_sriov_pf_service_print_versions },
153 };
154 
155 static void pf_populate_pf(struct xe_device *xe, struct dentry *pfdent)
156 {
157 	struct drm_minor *minor = xe->drm.primary;
158 
159 	drm_debugfs_create_files(debugfs_list, ARRAY_SIZE(debugfs_list), pfdent, minor);
160 }
161 
162 /*
163  *      /sys/kernel/debug/dri/BDF/
164  *      ├── sriov
165  *      │   ├── vf1
166  *      │   │   ├── migration_data
167  *      │   │   ├── pause
168  *      │   │   ├── reset
169  *      │   │   ├── resume
170  *      │   │   ├── stop
171  *      │   │   ├── save
172  *      │   │   ├── restore
173  *      │   │   :
174  *      │   ├── vf2
175  *      │   │   ├── ...
176  */
177 
178 static int from_file_read_to_vf_call(struct seq_file *s,
179 				     int (*call)(struct xe_device *, unsigned int))
180 {
181 	struct dentry *dent = file_dentry(s->file)->d_parent;
182 	struct xe_device *xe = extract_xe(dent);
183 	unsigned int vfid = extract_vfid(dent);
184 	int ret;
185 
186 	xe_pm_runtime_get(xe);
187 	ret = call(xe, vfid);
188 	xe_pm_runtime_put(xe);
189 
190 	if (ret < 0)
191 		return ret;
192 
193 	return 0;
194 }
195 
196 static ssize_t from_file_write_to_vf_call(struct file *file, const char __user *userbuf,
197 					  size_t count, loff_t *ppos,
198 					  int (*call)(struct xe_device *, unsigned int))
199 {
200 	struct dentry *dent = file_dentry(file)->d_parent;
201 	struct xe_device *xe = extract_xe(dent);
202 	unsigned int vfid = extract_vfid(dent);
203 	bool yes;
204 	int ret;
205 
206 	if (*ppos)
207 		return -EINVAL;
208 	ret = kstrtobool_from_user(userbuf, count, &yes);
209 	if (ret < 0)
210 		return ret;
211 	if (yes) {
212 		xe_pm_runtime_get(xe);
213 		ret = call(xe, vfid);
214 		xe_pm_runtime_put(xe);
215 	}
216 	if (ret < 0)
217 		return ret;
218 	return count;
219 }
220 
221 #define DEFINE_VF_CONTROL_ATTRIBUTE(OP)						\
222 static int OP##_show(struct seq_file *s, void *unused)				\
223 {										\
224 	return 0;								\
225 }										\
226 static ssize_t OP##_write(struct file *file, const char __user *userbuf,	\
227 			  size_t count, loff_t *ppos)				\
228 {										\
229 	return from_file_write_to_vf_call(file, userbuf, count, ppos,		\
230 					  xe_sriov_pf_control_##OP);		\
231 }										\
232 DEFINE_SHOW_STORE_ATTRIBUTE(OP)
233 
234 #define DEFINE_VF_CONTROL_ATTRIBUTE_RW(OP)					\
235 static int OP##_show(struct seq_file *s, void *unused)				\
236 {										\
237 	return from_file_read_to_vf_call(s,					\
238 					 xe_sriov_pf_control_finish_##OP);	\
239 }										\
240 static ssize_t OP##_write(struct file *file, const char __user *userbuf,	\
241 			  size_t count, loff_t *ppos)				\
242 {										\
243 	return from_file_write_to_vf_call(file, userbuf, count, ppos,		\
244 					  xe_sriov_pf_control_trigger_##OP);	\
245 }										\
246 DEFINE_SHOW_STORE_ATTRIBUTE(OP)
247 
248 DEFINE_VF_CONTROL_ATTRIBUTE(pause_vf);
249 DEFINE_VF_CONTROL_ATTRIBUTE(resume_vf);
250 DEFINE_VF_CONTROL_ATTRIBUTE(stop_vf);
251 DEFINE_VF_CONTROL_ATTRIBUTE(reset_vf);
252 DEFINE_VF_CONTROL_ATTRIBUTE_RW(save_vf);
253 DEFINE_VF_CONTROL_ATTRIBUTE_RW(restore_vf);
254 
255 static ssize_t data_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
256 {
257 	struct dentry *dent = file_dentry(file)->d_parent;
258 	struct xe_device *xe = extract_xe(dent);
259 	unsigned int vfid = extract_vfid(dent);
260 
261 	if (*pos)
262 		return -ESPIPE;
263 
264 	return xe_sriov_pf_migration_write(xe, vfid, buf, count);
265 }
266 
267 static ssize_t data_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
268 {
269 	struct dentry *dent = file_dentry(file)->d_parent;
270 	struct xe_device *xe = extract_xe(dent);
271 	unsigned int vfid = extract_vfid(dent);
272 
273 	if (*ppos)
274 		return -ESPIPE;
275 
276 	return xe_sriov_pf_migration_read(xe, vfid, buf, count);
277 }
278 
279 static const struct file_operations data_vf_fops = {
280 	.owner		= THIS_MODULE,
281 	.open		= simple_open,
282 	.write		= data_write,
283 	.read		= data_read,
284 	.llseek		= default_llseek,
285 };
286 
287 static ssize_t size_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
288 {
289 	struct dentry *dent = file_dentry(file)->d_parent;
290 	struct xe_device *xe = extract_xe(dent);
291 	unsigned int vfid = extract_vfid(dent);
292 	char buf[21];
293 	ssize_t ret;
294 	int len;
295 
296 	xe_pm_runtime_get(xe);
297 	ret = xe_sriov_pf_migration_size(xe, vfid);
298 	xe_pm_runtime_put(xe);
299 	if (ret < 0)
300 		return ret;
301 
302 	len = scnprintf(buf, sizeof(buf), "%zd\n", ret);
303 
304 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
305 }
306 
307 static const struct file_operations size_vf_fops = {
308 	.owner		= THIS_MODULE,
309 	.open		= simple_open,
310 	.read		= size_read,
311 	.llseek		= default_llseek,
312 };
313 
314 static void pf_populate_vf(struct xe_device *xe, struct dentry *vfdent)
315 {
316 	debugfs_create_file("pause", 0200, vfdent, xe, &pause_vf_fops);
317 	debugfs_create_file("resume", 0200, vfdent, xe, &resume_vf_fops);
318 	debugfs_create_file("stop", 0200, vfdent, xe, &stop_vf_fops);
319 	debugfs_create_file("reset", 0200, vfdent, xe, &reset_vf_fops);
320 	debugfs_create_file("save", 0600, vfdent, xe, &save_vf_fops);
321 	debugfs_create_file("restore", 0600, vfdent, xe, &restore_vf_fops);
322 	debugfs_create_file("migration_data", 0600, vfdent, xe, &data_vf_fops);
323 	debugfs_create_file("migration_size", 0400, vfdent, xe, &size_vf_fops);
324 }
325 
326 static void pf_populate_with_tiles(struct xe_device *xe, struct dentry *dent, unsigned int vfid)
327 {
328 	struct xe_tile *tile;
329 	unsigned int id;
330 
331 	for_each_tile(tile, xe, id)
332 		xe_tile_sriov_pf_debugfs_populate(tile, dent, vfid);
333 }
334 
335 /**
336  * xe_sriov_pf_debugfs_register - Register PF debugfs attributes.
337  * @xe: the &xe_device
338  * @root: the root &dentry
339  *
340  * Create separate directory that will contain all SR-IOV related files,
341  * organized per each SR-IOV function (PF, VF1, VF2, ..., VFn).
342  */
343 void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
344 {
345 	int totalvfs = xe_sriov_pf_get_totalvfs(xe);
346 	struct dentry *pfdent;
347 	struct dentry *vfdent;
348 	struct dentry *dent;
349 	char vfname[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */
350 	unsigned int n;
351 
352 	/*
353 	 *      /sys/kernel/debug/dri/BDF/
354 	 *      ├── sriov		# d_inode->i_private = (xe_device*)
355 	 *      │   ├── ...
356 	 */
357 	dent = debugfs_create_dir("sriov", root);
358 	if (IS_ERR(dent))
359 		return;
360 	dent->d_inode->i_private = xe;
361 
362 	pf_populate_root(xe, dent);
363 
364 	/*
365 	 *      /sys/kernel/debug/dri/BDF/
366 	 *      ├── sriov		# d_inode->i_private = (xe_device*)
367 	 *      │   ├── pf		# d_inode->i_private = (xe_device*)
368 	 *      │   │   ├── ...
369 	 */
370 	pfdent = debugfs_create_dir("pf", dent);
371 	if (IS_ERR(pfdent))
372 		return;
373 	pfdent->d_inode->i_private = xe;
374 
375 	pf_populate_pf(xe, pfdent);
376 	pf_populate_with_tiles(xe, pfdent, PFID);
377 
378 	/*
379 	 *      /sys/kernel/debug/dri/BDF/
380 	 *      ├── sriov		# d_inode->i_private = (xe_device*)
381 	 *      │   ├── vf1		# d_inode->i_private = VFID(1)
382 	 *      │   ├── vf2		# d_inode->i_private = VFID(2)
383 	 *      │   ├── ...
384 	 */
385 	for (n = 1; n <= totalvfs; n++) {
386 		snprintf(vfname, sizeof(vfname), "vf%u", VFID(n));
387 		vfdent = debugfs_create_dir(vfname, dent);
388 		if (IS_ERR(vfdent))
389 			return;
390 		vfdent->d_inode->i_private = (void *)(uintptr_t)VFID(n);
391 
392 		pf_populate_vf(xe, vfdent);
393 		pf_populate_with_tiles(xe, vfdent, VFID(n));
394 	}
395 }
396