xref: /linux/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c (revision e6c2b0f23221ed43c4cc6f636e9ab7862954d562)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2023-2024 Intel Corporation
4  */
5 
6 #include <linux/debugfs.h>
7 
8 #include <drm/drm_print.h>
9 #include <drm/drm_debugfs.h>
10 
11 #include "xe_bo.h"
12 #include "xe_debugfs.h"
13 #include "xe_device.h"
14 #include "xe_gt.h"
15 #include "xe_gt_debugfs.h"
16 #include "xe_gt_sriov_pf_config.h"
17 #include "xe_gt_sriov_pf_control.h"
18 #include "xe_gt_sriov_pf_debugfs.h"
19 #include "xe_gt_sriov_pf_helpers.h"
20 #include "xe_gt_sriov_pf_migration.h"
21 #include "xe_gt_sriov_pf_monitor.h"
22 #include "xe_gt_sriov_pf_policy.h"
23 #include "xe_gt_sriov_pf_service.h"
24 #include "xe_pm.h"
25 
26 /*
27  *      /sys/kernel/debug/dri/0/
28  *      ├── gt0		# d_inode->i_private = gt
29  *      │   ├── pf	# d_inode->i_private = gt
30  *      │   ├── vf1	# d_inode->i_private = VFID(1)
31  *      :   :
32  *      │   ├── vfN	# d_inode->i_private = VFID(N)
33  */
34 
35 static void *extract_priv(struct dentry *d)
36 {
37 	return d->d_inode->i_private;
38 }
39 
40 static struct xe_gt *extract_gt(struct dentry *d)
41 {
42 	return extract_priv(d->d_parent);
43 }
44 
45 static unsigned int extract_vfid(struct dentry *d)
46 {
47 	return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d);
48 }
49 
50 /*
51  *      /sys/kernel/debug/dri/0/
52  *      ├── gt0
53  *      │   ├── pf
54  *      │   │   ├── contexts_provisioned
55  *      │   │   ├── doorbells_provisioned
56  *      │   │   ├── runtime_registers
57  *      │   │   ├── negotiated_versions
58  *      │   │   ├── adverse_events
59  *      ├── gt1
60  *      │   ├── pf
61  *      │   │   ├── ...
62  */
63 
64 static const struct drm_info_list pf_info[] = {
65 	{
66 		"contexts_provisioned",
67 		.show = xe_gt_debugfs_simple_show,
68 		.data = xe_gt_sriov_pf_config_print_ctxs,
69 	},
70 	{
71 		"doorbells_provisioned",
72 		.show = xe_gt_debugfs_simple_show,
73 		.data = xe_gt_sriov_pf_config_print_dbs,
74 	},
75 	{
76 		"runtime_registers",
77 		.show = xe_gt_debugfs_simple_show,
78 		.data = xe_gt_sriov_pf_service_print_runtime,
79 	},
80 	{
81 		"adverse_events",
82 		.show = xe_gt_debugfs_simple_show,
83 		.data = xe_gt_sriov_pf_monitor_print_events,
84 	},
85 };
86 
87 /*
88  *      /sys/kernel/debug/dri/0/
89  *      ├── gt0
90  *      │   ├── pf
91  *      │   │   ├── ggtt_available
92  *      │   │   ├── ggtt_provisioned
93  */
94 
95 static const struct drm_info_list pf_ggtt_info[] = {
96 	{
97 		"ggtt_available",
98 		.show = xe_gt_debugfs_simple_show,
99 		.data = xe_gt_sriov_pf_config_print_available_ggtt,
100 	},
101 	{
102 		"ggtt_provisioned",
103 		.show = xe_gt_debugfs_simple_show,
104 		.data = xe_gt_sriov_pf_config_print_ggtt,
105 	},
106 };
107 
108 /*
109  *      /sys/kernel/debug/dri/0/
110  *      ├── gt0
111  *      │   ├── pf
112  *      │   │   ├── lmem_provisioned
113  */
114 
115 static const struct drm_info_list pf_lmem_info[] = {
116 	{
117 		"lmem_provisioned",
118 		.show = xe_gt_debugfs_simple_show,
119 		.data = xe_gt_sriov_pf_config_print_lmem,
120 	},
121 };
122 
123 /*
124  *      /sys/kernel/debug/dri/0/
125  *      ├── gt0
126  *      │   ├── pf
127  *      │   │   ├── reset_engine
128  *      │   │   ├── sample_period
129  *      │   │   ├── sched_if_idle
130  */
131 
132 #define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT)		\
133 										\
134 static int POLICY##_set(void *data, u64 val)					\
135 {										\
136 	struct xe_gt *gt = extract_gt(data);					\
137 	struct xe_device *xe = gt_to_xe(gt);					\
138 	int err;								\
139 										\
140 	if (val > (TYPE)~0ull)							\
141 		return -EOVERFLOW;						\
142 										\
143 	xe_pm_runtime_get(xe);							\
144 	err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val);			\
145 	xe_pm_runtime_put(xe);							\
146 										\
147 	return err;								\
148 }										\
149 										\
150 static int POLICY##_get(void *data, u64 *val)					\
151 {										\
152 	struct xe_gt *gt = extract_gt(data);					\
153 										\
154 	*val = xe_gt_sriov_pf_policy_get_##POLICY(gt);				\
155 	return 0;								\
156 }										\
157 										\
158 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT)
159 
160 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n");
161 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n");
162 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n");
163 
164 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
165 {
166 	xe_gt_assert(gt, gt == extract_gt(parent));
167 	xe_gt_assert(gt, PFID == extract_vfid(parent));
168 
169 	debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops);
170 	debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops);
171 	debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops);
172 }
173 
174 /*
175  *      /sys/kernel/debug/dri/0/
176  *      ├── gt0
177  *      │   ├── pf
178  *      │   │   ├── ggtt_spare
179  *      │   │   ├── lmem_spare
180  *      │   │   ├── doorbells_spare
181  *      │   │   ├── contexts_spare
182  *      │   │   ├── exec_quantum_ms
183  *      │   │   ├── preempt_timeout_us
184  *      │   │   ├── sched_priority
185  *      │   ├── vf1
186  *      │   │   ├── ggtt_quota
187  *      │   │   ├── lmem_quota
188  *      │   │   ├── doorbells_quota
189  *      │   │   ├── contexts_quota
190  *      │   │   ├── exec_quantum_ms
191  *      │   │   ├── preempt_timeout_us
192  *      │   │   ├── sched_priority
193  */
194 
195 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT)		\
196 										\
197 static int CONFIG##_set(void *data, u64 val)					\
198 {										\
199 	struct xe_gt *gt = extract_gt(data);					\
200 	unsigned int vfid = extract_vfid(data);					\
201 	struct xe_device *xe = gt_to_xe(gt);					\
202 	int err;								\
203 										\
204 	if (val > (TYPE)~0ull)							\
205 		return -EOVERFLOW;						\
206 										\
207 	xe_pm_runtime_get(xe);							\
208 	err = xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val);		\
209 	xe_pm_runtime_put(xe);							\
210 										\
211 	return err;								\
212 }										\
213 										\
214 static int CONFIG##_get(void *data, u64 *val)					\
215 {										\
216 	struct xe_gt *gt = extract_gt(data);					\
217 	unsigned int vfid = extract_vfid(data);					\
218 										\
219 	*val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid);			\
220 	return 0;								\
221 }										\
222 										\
223 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT)
224 
225 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n");
226 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n");
227 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
228 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
229 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
230 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
231 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n");
232 
233 /*
234  *      /sys/kernel/debug/dri/0/
235  *      ├── gt0
236  *      │   ├── pf
237  *      │   │   ├── threshold_cat_error_count
238  *      │   │   ├── threshold_doorbell_time_us
239  *      │   │   ├── threshold_engine_reset_count
240  *      │   │   ├── threshold_guc_time_us
241  *      │   │   ├── threshold_irq_time_us
242  *      │   │   ├── threshold_page_fault_count
243  *      │   ├── vf1
244  *      │   │   ├── threshold_cat_error_count
245  *      │   │   ├── threshold_doorbell_time_us
246  *      │   │   ├── threshold_engine_reset_count
247  *      │   │   ├── threshold_guc_time_us
248  *      │   │   ├── threshold_irq_time_us
249  *      │   │   ├── threshold_page_fault_count
250  */
251 
252 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index)
253 {
254 	struct xe_gt *gt = extract_gt(data);
255 	unsigned int vfid = extract_vfid(data);
256 	struct xe_device *xe = gt_to_xe(gt);
257 	int err;
258 
259 	if (val > (u32)~0ull)
260 		return -EOVERFLOW;
261 
262 	xe_pm_runtime_get(xe);
263 	err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
264 	xe_pm_runtime_put(xe);
265 
266 	return err;
267 }
268 
269 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index)
270 {
271 	struct xe_gt *gt = extract_gt(data);
272 	unsigned int vfid = extract_vfid(data);
273 
274 	*val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index);
275 	return 0;
276 }
277 
278 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX)		\
279 										\
280 static int THRESHOLD##_set(void *data, u64 val)					\
281 {										\
282 	return set_threshold(data, val, INDEX);					\
283 }										\
284 										\
285 static int THRESHOLD##_get(void *data, u64 *val)				\
286 {										\
287 	return get_threshold(data, val, INDEX);					\
288 }										\
289 										\
290 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n")
291 
292 /* generate all threshold attributes */
293 #define define_threshold_attribute(TAG, NAME, ...) \
294 	DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG));
295 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute)
296 #undef define_threshold_attribute
297 
298 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
299 {
300 	xe_gt_assert(gt, gt == extract_gt(parent));
301 	xe_gt_assert(gt, vfid == extract_vfid(parent));
302 
303 	if (xe_gt_is_main_type(gt)) {
304 		debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
305 					   0644, parent, parent, &ggtt_fops);
306 		if (xe_device_has_lmtt(gt_to_xe(gt)))
307 			debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare",
308 						   0644, parent, parent, &lmem_fops);
309 	}
310 	debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare",
311 				   0644, parent, parent, &dbs_fops);
312 	debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare",
313 				   0644, parent, parent, &ctxs_fops);
314 	debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent,
315 				   &exec_quantum_fops);
316 	debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent,
317 				   &preempt_timeout_fops);
318 	debugfs_create_file_unsafe("sched_priority", 0644, parent, parent,
319 				   &sched_priority_fops);
320 
321 	/* register all threshold attributes */
322 #define register_threshold_attribute(TAG, NAME, ...) \
323 	debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
324 				   &NAME##_fops);
325 	MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute)
326 #undef register_threshold_attribute
327 }
328 
329 /*
330  *      /sys/kernel/debug/dri/0/
331  *      ├── gt0
332  *      │   ├── vf1
333  *      │   │   ├── control { stop, pause, resume }
334  */
335 
336 static const struct {
337 	const char *cmd;
338 	int (*fn)(struct xe_gt *gt, unsigned int vfid);
339 } control_cmds[] = {
340 	{ "stop", xe_gt_sriov_pf_control_stop_vf },
341 	{ "pause", xe_gt_sriov_pf_control_pause_vf },
342 	{ "resume", xe_gt_sriov_pf_control_resume_vf },
343 #ifdef CONFIG_DRM_XE_DEBUG_SRIOV
344 	{ "restore!", xe_gt_sriov_pf_migration_restore_guc_state },
345 #endif
346 };
347 
348 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
349 {
350 	struct dentry *dent = file_dentry(file);
351 	struct dentry *parent = dent->d_parent;
352 	struct xe_gt *gt = extract_gt(parent);
353 	struct xe_device *xe = gt_to_xe(gt);
354 	unsigned int vfid = extract_vfid(parent);
355 	int ret = -EINVAL;
356 	char cmd[32];
357 	size_t n;
358 
359 	xe_gt_assert(gt, vfid);
360 	xe_gt_sriov_pf_assert_vfid(gt, vfid);
361 
362 	if (*pos)
363 		return -ESPIPE;
364 
365 	if (count > sizeof(cmd) - 1)
366 		return -EINVAL;
367 
368 	ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count);
369 	if (ret < 0)
370 		return ret;
371 	cmd[ret] = '\0';
372 
373 	for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
374 		xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd));
375 
376 		if (sysfs_streq(cmd, control_cmds[n].cmd)) {
377 			xe_pm_runtime_get(xe);
378 			ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0;
379 			xe_pm_runtime_put(xe);
380 			break;
381 		}
382 	}
383 
384 	return (ret < 0) ? ret : count;
385 }
386 
387 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
388 {
389 	char help[128];
390 	size_t n;
391 
392 	help[0] = '\0';
393 	for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
394 		strlcat(help, control_cmds[n].cmd, sizeof(help));
395 		strlcat(help, "\n", sizeof(help));
396 	}
397 
398 	return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
399 }
400 
401 static const struct file_operations control_ops = {
402 	.owner		= THIS_MODULE,
403 	.open		= simple_open,
404 	.write		= control_write,
405 	.read		= control_read,
406 	.llseek		= default_llseek,
407 };
408 
409 /*
410  *      /sys/kernel/debug/dri/0/
411  *      ├── gt0
412  *      │   ├── vf1
413  *      │   │   ├── guc_state
414  */
415 static ssize_t guc_state_read(struct file *file, char __user *buf,
416 			      size_t count, loff_t *pos)
417 {
418 	struct dentry *dent = file_dentry(file);
419 	struct dentry *parent = dent->d_parent;
420 	struct xe_gt *gt = extract_gt(parent);
421 	unsigned int vfid = extract_vfid(parent);
422 
423 	return xe_gt_sriov_pf_migration_read_guc_state(gt, vfid, buf, count, pos);
424 }
425 
426 static ssize_t guc_state_write(struct file *file, const char __user *buf,
427 			       size_t count, loff_t *pos)
428 {
429 	struct dentry *dent = file_dentry(file);
430 	struct dentry *parent = dent->d_parent;
431 	struct xe_gt *gt = extract_gt(parent);
432 	unsigned int vfid = extract_vfid(parent);
433 
434 	if (*pos)
435 		return -EINVAL;
436 
437 	return xe_gt_sriov_pf_migration_write_guc_state(gt, vfid, buf, count);
438 }
439 
440 static const struct file_operations guc_state_ops = {
441 	.owner		= THIS_MODULE,
442 	.read		= guc_state_read,
443 	.write		= guc_state_write,
444 	.llseek		= default_llseek,
445 };
446 
447 /*
448  *      /sys/kernel/debug/dri/0/
449  *      ├── gt0
450  *      │   ├── vf1
451  *      │   │   ├── config_blob
452  */
453 static ssize_t config_blob_read(struct file *file, char __user *buf,
454 				size_t count, loff_t *pos)
455 {
456 	struct dentry *dent = file_dentry(file);
457 	struct dentry *parent = dent->d_parent;
458 	struct xe_gt *gt = extract_gt(parent);
459 	unsigned int vfid = extract_vfid(parent);
460 	ssize_t ret;
461 	void *tmp;
462 
463 	ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0);
464 	if (!ret)
465 		return -ENODATA;
466 	if (ret < 0)
467 		return ret;
468 
469 	tmp = kzalloc(ret, GFP_KERNEL);
470 	if (!tmp)
471 		return -ENOMEM;
472 
473 	ret = xe_gt_sriov_pf_config_save(gt, vfid, tmp, ret);
474 	if (ret > 0)
475 		ret = simple_read_from_buffer(buf, count, pos, tmp, ret);
476 
477 	kfree(tmp);
478 	return ret;
479 }
480 
481 static ssize_t config_blob_write(struct file *file, const char __user *buf,
482 				 size_t count, loff_t *pos)
483 {
484 	struct dentry *dent = file_dentry(file);
485 	struct dentry *parent = dent->d_parent;
486 	struct xe_gt *gt = extract_gt(parent);
487 	unsigned int vfid = extract_vfid(parent);
488 	ssize_t ret;
489 	void *tmp;
490 
491 	if (*pos)
492 		return -EINVAL;
493 
494 	if (!count)
495 		return -ENODATA;
496 
497 	if (count > SZ_4K)
498 		return -EINVAL;
499 
500 	tmp = kzalloc(count, GFP_KERNEL);
501 	if (!tmp)
502 		return -ENOMEM;
503 
504 	if (copy_from_user(tmp, buf, count)) {
505 		ret = -EFAULT;
506 	} else {
507 		ret = xe_gt_sriov_pf_config_restore(gt, vfid, tmp, count);
508 		if (!ret)
509 			ret = count;
510 	}
511 	kfree(tmp);
512 	return ret;
513 }
514 
515 static const struct file_operations config_blob_ops = {
516 	.owner		= THIS_MODULE,
517 	.read		= config_blob_read,
518 	.write		= config_blob_write,
519 	.llseek		= default_llseek,
520 };
521 
522 /**
523  * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
524  * @gt: the &xe_gt to register
525  * @root: the &dentry that represents the GT directory
526  *
527  * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs.
528  */
529 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root)
530 {
531 	struct xe_device *xe = gt_to_xe(gt);
532 	struct drm_minor *minor = xe->drm.primary;
533 	int n, totalvfs = xe_sriov_pf_get_totalvfs(xe);
534 	struct dentry *pfdentry;
535 	struct dentry *vfdentry;
536 	char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */
537 
538 	xe_gt_assert(gt, IS_SRIOV_PF(xe));
539 	xe_gt_assert(gt, root->d_inode->i_private == gt);
540 
541 	/*
542 	 *      /sys/kernel/debug/dri/0/
543 	 *      ├── gt0
544 	 *      │   ├── pf
545 	 */
546 	pfdentry = debugfs_create_dir("pf", root);
547 	if (IS_ERR(pfdentry))
548 		return;
549 	pfdentry->d_inode->i_private = gt;
550 
551 	drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor);
552 	if (xe_gt_is_main_type(gt)) {
553 		drm_debugfs_create_files(pf_ggtt_info,
554 					 ARRAY_SIZE(pf_ggtt_info),
555 					 pfdentry, minor);
556 		if (xe_device_has_lmtt(gt_to_xe(gt)))
557 			drm_debugfs_create_files(pf_lmem_info,
558 						 ARRAY_SIZE(pf_lmem_info),
559 						 pfdentry, minor);
560 	}
561 
562 	pf_add_policy_attrs(gt, pfdentry);
563 	pf_add_config_attrs(gt, pfdentry, PFID);
564 
565 	for (n = 1; n <= totalvfs; n++) {
566 		/*
567 		 *      /sys/kernel/debug/dri/0/
568 		 *      ├── gt0
569 		 *      │   ├── vf1
570 		 *      │   ├── vf2
571 		 */
572 		snprintf(buf, sizeof(buf), "vf%u", n);
573 		vfdentry = debugfs_create_dir(buf, root);
574 		if (IS_ERR(vfdentry))
575 			break;
576 		vfdentry->d_inode->i_private = (void *)(uintptr_t)n;
577 
578 		pf_add_config_attrs(gt, vfdentry, VFID(n));
579 		debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops);
580 
581 		/* for testing/debugging purposes only! */
582 		if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
583 			debugfs_create_file("guc_state",
584 					    IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
585 					    vfdentry, NULL, &guc_state_ops);
586 			debugfs_create_file("config_blob",
587 					    IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
588 					    vfdentry, NULL, &config_blob_ops);
589 		}
590 	}
591 }
592